feat(query): stream extracted questions immediately via SSE

Convert /query endpoint from synchronous JSON to Server-Sent Events (SSE)
streaming. The frontend now receives extracted_questions as soon as the
first LLM call completes, without waiting for retrieval, filtering, and
answer generation.

Backend:
- Add StreamingQueryEvent union type (Decomposed, Retrieving, Filtering,
  Generating, Completed, Error)
- Convert /query to return StreamingResponse with SSE format
- Yield events after each pipeline phase

Frontend:
- Add queryDocumentStream() using fetch + ReadableStream
- Add useQueryDocumentStream() hook with phase-aware state
- Update LTTPage to use streaming instead of mutation
- Update ResponsePanel to show phase messages (Searching documents...,
  Filtering passages..., Generating answer...)
- Update ExtractedQuestionsDisplay to accept null

Tests:
- Update query_flow e2e test to mock queryDocumentStream
- 84/85 tests pass (1 pre-existing failure from removed file-input)
This commit is contained in:
Woody 2026-04-25 18:29:22 +08:00
parent 15b17a74ff
commit 3b741c1844
31 changed files with 1819 additions and 34 deletions

View File

@ -0,0 +1,454 @@
# Plan: Show Extracted Questions Immediately (Streaming Query Response)
## Problem
On the LTT page, when a user submits a question like "what is power of project manager", the "Extracted Questions" section only appears after **all three LLM calls** complete:
1. **QueryDecomposer** (LLM #1) → `extracted_questions` — **available here**
2. **RelevanceFilter** (LLM #2) → filtered chunks
3. **ResponseGeneration** (LLM #3) → final answer
The user must wait 10-30+ seconds (depending on LLM latency) before seeing the extracted sub-questions, even though they are produced in the first ~3-5 seconds.
## Root Cause
The backend `/query` endpoint is a single synchronous HTTP call that awaits all pipeline steps before returning the complete `QueryResponse`. The frontend's `useMutation` receives the entire response at once.
## Objective
Show `extracted_questions` to the user **as soon as the first LLM call completes**, while the remaining pipeline steps continue in the background. The user sees:
1. Extracted questions immediately (after LLM #1)
2. A loading indicator for the answer (while LLM #2 and #3 run)
3. The final answer and sources when complete
## Recommended Approach: SSE Streaming
Convert the `/query` endpoint to use **Server-Sent Events (SSE)** — the industry-standard pattern for streaming LLM responses. This requires changes to both backend and frontend.
### Why SSE (not WebSocket or separate endpoint)
| Approach | Pros | Cons | Verdict |
|----------|------|------|---------|
| **SSE Streaming** | Single connection, automatic reconnect, HTTP-based, simple JSON lines | One-directional (server→client only) | ✅ **Best fit** — we only need server→client |
| **WebSocket** | Bidirectional, lower overhead | Overkill for this use case, more complex | ❌ Unnecessary complexity |
| **Separate `/decompose` endpoint** | Minimal backend change | Two HTTP round-trips, awkward UX (submit twice), harder to maintain state | ❌ Poor UX, more frontend complexity |
## Backend Changes
### 1. New Streaming Response Models
**File**: `backend/app/models/query.py`
Add a `StreamingQueryEvent` union type for SSE events:
```python
from pydantic import BaseModel
from typing import Literal, Union
class DecomposedEvent(BaseModel):
phase: Literal["decomposed"]
extracted_questions: list[str]
class RetrievingEvent(BaseModel):
phase: Literal["retrieving"]
class FilteringEvent(BaseModel):
phase: Literal["filtering"]
class CompletedEvent(BaseModel):
phase: Literal["completed"]
answer: str
sources: list[SourceMetadata]
class ErrorEvent(BaseModel):
phase: Literal["error"]
message: str
StreamingQueryEvent = Union[
DecomposedEvent, RetrievingEvent, FilteringEvent,
CompletedEvent, ErrorEvent
]
```
### 2. Streaming Query Endpoint
**File**: `backend/app/routers/query.py`
Convert `/query` to return `StreamingResponse` with `text/event-stream`:
```python
from fastapi.responses import StreamingResponse
import json
async def _query_stream(request: QueryRequest):
"""Generator that yields SSE events for the query pipeline."""
settings = get_settings()
try:
llm_client = LLMClient(settings)
rag = RAGService(llm_client=llm_client, settings=settings)
# Step 1: Decompose (LLM #1)
decomposer = QueryDecomposer(llm_client)
extracted_questions = await decomposer.decompose(request.question)
yield f"data: {json.dumps({'phase': 'decomposed', 'extracted_questions': extracted_questions})}\n\n"
# Step 2: Retrieve (ChromaDB)
yield f"data: {json.dumps({'phase': 'retrieving'})}\n\n"
chunks = rag.retrieve(extracted_questions, n_results=settings.retrieval_n_results)
if not chunks:
yield f"data: {json.dumps({'phase': 'completed', 'answer': NO_RESULTS_ANSWER, 'sources': []})}\n\n"
return
# Step 3: Filter (LLM #2)
yield f"data: {json.dumps({'phase': 'filtering'})}\n\n"
chunks_for_filter = [(text, meta) for text, meta, _dist in chunks]
relevance_filter = RelevanceFilter(llm_client)
filtered = await relevance_filter.filter(
request.question, chunks_for_filter, threshold=settings.relevance_threshold
)
if not filtered:
yield f"data: {json.dumps({'phase': 'completed', 'answer': NO_RESULTS_ANSWER, 'sources': []})}\n\n"
return
# Step 4: Generate (LLM #3)
chunk_texts = [chunk for chunk, _meta in filtered]
chunk_metadata = [meta for _chunk, meta in filtered]
answer = await rag.generate_response(request.question, chunk_texts, chunk_metadata)
sources = [
SourceMetadata(
filename=meta.get("filename", "unknown"),
upload_date=meta.get("upload_date", ""),
content_summary=meta.get("content_summary", ""),
chunk_index=meta.get("chunk_index", 0),
page_number=meta.get("page_number"),
chunk_file_path=meta.get("chunk_file_path"),
)
for meta in chunk_metadata
]
yield f"data: {json.dumps({'phase': 'completed', 'answer': answer, 'sources': [s.model_dump() for s in sources]})}\n\n"
except Exception as e:
logger.error("Query stream failed: %s", str(e))
yield f"data: {json.dumps({'phase': 'error', 'message': str(e)})}\n\n"
@router.post("/query")
async def query(request: QueryRequest):
if not request.question or not request.question.strip():
raise HTTPException(status_code=400, detail="Question is required")
return StreamingResponse(
_query_stream(request),
media_type="text/event-stream",
)
```
### 3. Keep Old Endpoint (Backward Compatibility)
Add a new `/query/stream` endpoint and keep the existing `/query` for backward compatibility, or version the API. For simplicity in this plan, we'll replace `/query` but the implementation could support both.
**Decision**: Replace `/query` with streaming. The frontend is the only consumer and will be updated together.
## Frontend Changes
### 1. New Streaming API Client
**File**: `frontend/src/lib/api.ts`
Add a streaming query function:
```typescript
export interface QueryStreamEvent {
phase: 'decomposed' | 'retrieving' | 'filtering' | 'completed' | 'error'
extracted_questions?: string[]
answer?: string
sources?: SourceMetadata[]
message?: string
}
export const queryDocumentStream = (
request: QueryRequest,
onEvent: (event: QueryStreamEvent) => void,
onError: (error: Error) => void,
onComplete: () => void
): (() => void) => {
const baseUrl = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8000/api/v1'
const url = `${baseUrl}/query`
const eventSource = new EventSource(url, {
// EventSource doesn't support POST with body natively
// We need to use fetch + ReadableStream or a polyfill
})
// ... implementation using fetch + ReadableStream
return () => eventSource.close() // cleanup function
}
```
**Important**: Standard `EventSource` only supports GET, not POST. We need to use `fetch()` with `ReadableStream` to POST the question and read SSE events.
```typescript
export const queryDocumentStream = async (
request: QueryRequest,
onEvent: (event: QueryStreamEvent) => void
): Promise<void> => {
const baseUrl = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8000/api/v1'
const response = await fetch(`${baseUrl}/query`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`)
}
const reader = response.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') return
onEvent(JSON.parse(data))
}
}
}
}
```
### 2. New React Hook for Streaming
**File**: `frontend/src/lib/queries.tsx`
Add a custom hook that manages streaming state:
```typescript
import { useState, useCallback, useRef } from 'react'
interface QueryStreamState {
extractedQuestions: string[] | null
answer: string | null
sources: SourceMetadata[] | null
phase: 'idle' | 'decomposing' | 'retrieving' | 'filtering' | 'generating' | 'completed' | 'error'
error: Error | null
}
export const useQueryDocumentStream = () => {
const [state, setState] = useState<QueryStreamState>({
extractedQuestions: null,
answer: null,
sources: null,
phase: 'idle',
error: null,
})
const abortRef = useRef<AbortController | null>(null)
const mutate = useCallback(async (request: QueryRequest) => {
setState({
extractedQuestions: null,
answer: null,
sources: null,
phase: 'decomposing',
error: null,
})
abortRef.current = new AbortController()
try {
await queryDocumentStream(request, (event) => {
switch (event.phase) {
case 'decomposed':
setState(prev => ({
...prev,
extractedQuestions: event.extracted_questions!,
phase: 'retrieving',
}))
break
case 'retrieving':
setState(prev => ({ ...prev, phase: 'retrieving' }))
break
case 'filtering':
setState(prev => ({ ...prev, phase: 'filtering' }))
break
case 'completed':
setState(prev => ({
...prev,
answer: event.answer!,
sources: event.sources!,
phase: 'completed',
}))
break
case 'error':
setState(prev => ({
...prev,
phase: 'error',
error: new Error(event.message!),
}))
break
}
})
} catch (err) {
setState(prev => ({
...prev,
phase: 'error',
error: err instanceof Error ? err : new Error(String(err)),
}))
}
}, [])
const reset = useCallback(() => {
abortRef.current?.abort()
setState({
extractedQuestions: null,
answer: null,
sources: null,
phase: 'idle',
error: null,
})
}, [])
return { ...state, mutate, reset }
}
```
### 3. Update LTTPage to Use Streaming
**File**: `frontend/src/pages/LTTPage.tsx`
Replace `useQueryDocument` with `useQueryDocumentStream`:
```typescript
const queryStream = useQueryDocumentStream()
const handleQuerySubmit = (question: string) => {
queryStream.mutate({ question })
}
// In JSX:
<ExtractedQuestionsDisplay
extractedQuestions={queryStream.extractedQuestions}
isLoading={queryStream.phase === 'decomposing'}
/>
<ResponsePanel
answer={queryStream.answer}
sources={queryStream.sources}
isLoading={queryStream.phase === 'retrieving' || queryStream.phase === 'filtering' || queryStream.phase === 'generating'}
extractedQuestions={queryStream.extractedQuestions}
/>
```
### 4. Update ExtractedQuestionsDisplay
**File**: `frontend/src/components/ExtractedQuestionsDisplay.tsx`
The component should already work with the new state shape. Ensure it handles the case where `extractedQuestions` is available but `answer` is still loading:
```typescript
// Show questions as soon as they arrive, even if answer is still loading
if (extractedQuestions && extractedQuestions.length > 0) {
return (
<div>
<h3>Extracted Questions:</h3>
<ol>
{extractedQuestions.map((q, i) => <li key={i}>{q}</li>)}
</ol>
</div>
)
}
```
### 5. Update ResponsePanel Loading States
**File**: `frontend/src/components/ResponsePanel.tsx`
Show different loading states based on phase:
- `phase === 'retrieving'`: "Searching documents..."
- `phase === 'filtering'`: "Filtering relevant passages..."
- `phase === 'generating'`: "Generating answer..."
## Implementation Order
1. **Backend streaming endpoint** (`backend/app/routers/query.py`)
- Add `_query_stream()` generator
- Update `/query` to return `StreamingResponse`
- Test with `curl` to verify SSE format
2. **Frontend streaming API** (`frontend/src/lib/api.ts`)
- Add `queryDocumentStream()` function
- Add `QueryStreamEvent` type
3. **Frontend streaming hook** (`frontend/src/lib/queries.tsx`)
- Add `useQueryDocumentStream()` hook
4. **Update LTTPage** (`frontend/src/pages/LTTPage.tsx`)
- Replace `useQueryDocument` with `useQueryDocumentStream`
- Wire up new state to components
5. **Update ResponsePanel** (`frontend/src/components/ResponsePanel.tsx`)
- Add phase-aware loading messages
6. **Tests**
- Mock `fetch` with `ReadableStream` for testing
- Test each phase transition
- Test error handling
## Testing Strategy
### Backend Tests
- Test SSE event format matches expected JSON structure
- Test each phase event is emitted in correct order
- Test error events on failures
- Test early exit (no chunks) still emits `decomposed` then `completed`
### Frontend Tests
- Mock `fetch` to return a `ReadableStream` with SSE data
- Test `useQueryDocumentStream` state transitions:
- idle → decomposing → decomposed (extractedQuestions set) → retrieving → filtering → completed (answer set)
- Test error handling: stream emits error event → state.error set
- Test abort: calling reset() aborts in-flight request
## Risks & Mitigations
| Risk | Mitigation |
|------|-----------|
| SSE not supported by older browsers | Use fetch + ReadableStream (works in all modern browsers) |
| Proxy/CDN buffering SSE | Set `X-Accel-Buffering: no` header, use `Cache-Control: no-cache` |
| CORS with streaming | Ensure `Access-Control-Allow-Origin` includes frontend origin |
| Connection drops mid-stream | Add retry logic or show "Connection lost" message |
| TanStack Query cache invalidation | Not needed — streaming bypasses cache, keep old `useQueryDocument` for non-streaming use |
## Files to Modify
| File | Change |
|------|--------|
| `backend/app/models/query.py` | Add streaming event models |
| `backend/app/routers/query.py` | Convert to StreamingResponse |
| `frontend/src/lib/api.ts` | Add `queryDocumentStream()` |
| `frontend/src/lib/queries.tsx` | Add `useQueryDocumentStream()` hook |
| `frontend/src/pages/LTTPage.tsx` | Use streaming hook |
| `frontend/src/components/ResponsePanel.tsx` | Phase-aware loading states |
## Success Criteria
- [ ] Extracted questions appear within 5 seconds of submitting a query
- [ ] Answer appears when generation completes (no UI blocking)
- [ ] Loading states clearly indicate which pipeline step is running
- [ ] Error handling works (shows error message if any step fails)
- [ ] All existing tests pass (or are updated for new behavior)
- [ ] New tests cover streaming state transitions

View File

@ -0,0 +1,14 @@
- generic [ref=e3]:
- generic [ref=e6]:
- img [ref=e7]
- generic [ref=e9]: Video upload coming in Phase 2
- generic [ref=e10]:
- generic [ref=e11]:
- textbox "Ask a question about your documents..." [ref=e12]
- button "Submit" [disabled] [ref=e13]
- generic [ref=e15] [cursor=pointer]:
- img [ref=e16]
- generic [ref=e19]: Upload Document
- generic [ref=e22]:
- img [ref=e23]
- generic [ref=e25]: Ask a question to see the answer here.

View File

@ -0,0 +1,14 @@
- generic [ref=e3]:
- generic [ref=e6]:
- img [ref=e7]
- generic [ref=e9]: Video upload coming in Phase 2
- generic [ref=e10]:
- generic [ref=e11]:
- textbox "Ask a question about your documents..." [ref=e12]
- button "Submit" [disabled] [ref=e13]
- generic [ref=e15] [cursor=pointer]:
- img [ref=e16]
- generic [ref=e19]: Upload Document
- generic [ref=e22]:
- img [ref=e23]
- generic [ref=e25]: Ask a question to see the answer here.

View File

@ -0,0 +1,18 @@
- generic [ref=e3]:
- generic [ref=e6]:
- img [ref=e7]
- generic [ref=e9]: Video upload coming in Phase 2
- generic [ref=e10]:
- generic [ref=e11]:
- textbox "Ask a question about your documents..." [ref=e12]
- button "Submit" [disabled] [ref=e13]
- generic [ref=e14]:
- generic [ref=e15] [cursor=pointer]:
- img [ref=e26]
- generic [ref=e19]: Upload Document
- generic [ref=e29]:
- img [ref=e30]
- generic [ref=e32]: Request failed with status code 500
- generic [ref=e22]:
- img [ref=e23]
- generic [ref=e25]: Ask a question to see the answer here.

View File

@ -0,0 +1,6 @@
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database

View File

@ -0,0 +1,21 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e9]:
- generic [ref=e12]:
- img [ref=e13]
- generic [ref=e15]: Video upload coming in Phase 2
- generic [ref=e16]:
- generic [ref=e17]:
- textbox "Ask a question about your documents..." [ref=e18]
- button "Submit" [disabled] [ref=e19]
- generic [ref=e21] [cursor=pointer]:
- img [ref=e22]
- generic [ref=e25]: Upload Document
- generic [ref=e28]:
- img [ref=e29]
- generic [ref=e31]: Ask a question to see the answer here.

View File

@ -0,0 +1,21 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e9]:
- generic [ref=e12]:
- img [ref=e13]
- generic [ref=e15]: Video upload coming in Phase 2
- generic [ref=e16]:
- generic [ref=e17]:
- textbox "Ask a question about your documents..." [ref=e18]
- button "Submit" [disabled] [ref=e19]
- generic [ref=e21] [cursor=pointer]:
- img [ref=e32]
- generic [ref=e25]: Uploading...
- generic [ref=e28]:
- img [ref=e29]
- generic [ref=e31]: Ask a question to see the answer here.

View File

@ -0,0 +1,26 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [active] [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e42]:
- generic [ref=e44]:
- heading "RAG Database" [level=1] [ref=e45]
- generic [ref=e47] [cursor=pointer]:
- img [ref=e48]
- generic [ref=e51]: Upload
- generic [ref=e53]: "Total: 1 documents, 97 chunks"
- generic [ref=e59]:
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e64]:
- generic [ref=e65]: tmp90i7xqa8.pdf
- generic [ref=e66]: 97 chunks • Uploaded 2026-04-24T10:01:00.458015
- generic [ref=e67]:
- button "View Chunks" [ref=e68] [cursor=pointer]:
- img [ref=e69]
- generic [ref=e71]: View Chunks
- button [ref=e72] [cursor=pointer]:
- img [ref=e73]

View File

@ -0,0 +1,804 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e42]:
- generic [ref=e44]:
- heading "RAG Database" [level=1] [ref=e45]
- generic [ref=e47] [cursor=pointer]:
- img [ref=e48]
- generic [ref=e51]: Upload
- generic [ref=e53]: "Total: 1 documents, 97 chunks"
- generic [ref=e56]:
- generic [ref=e59]:
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e64]:
- generic [ref=e65]: tmp90i7xqa8.pdf
- generic [ref=e66]: 97 chunks • Uploaded 2026-04-24T10:01:00.458015
- generic [ref=e67]:
- button "Hide Chunks" [active] [ref=e76] [cursor=pointer]:
- img [ref=e77]
- generic [ref=e71]: Hide Chunks
- button [ref=e72] [cursor=pointer]:
- img [ref=e73]
- generic [ref=e80]:
- generic [ref=e81]:
- generic [ref=e82]:
- generic [ref=e83]:
- generic [ref=e84]: Chunk 0
- generic [ref=e85]: "Page: N/A"
- generic "Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 1 of 187 Section A Definitions and Contract Documents A1 Unless otherwise specified, the terms and definitio" [ref=e86]: Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 1 of 187 Secti...
- button [ref=e87] [cursor=pointer]:
- img [ref=e88]
- generic [ref=e91]:
- generic [ref=e92]:
- generic [ref=e93]:
- generic [ref=e94]: Chunk 1
- generic [ref=e95]: "Page: N/A"
- generic "“Particular Specification ” means the “Particular Specification” forming Annex [insert appropriate reference] to the Scope. Commonly adopted in NEC contracts A1 (Contd) “Site Workers”" [ref=e96]: “Particular Specification ” means the “Particular Specification” forming Annex [insert appropriate...
- button [ref=e97] [cursor=pointer]:
- img [ref=e98]
- generic [ref=e101]:
- generic [ref=e102]:
- generic [ref=e103]:
- generic [ref=e104]: Chunk 2
- generic [ref=e105]: "Page: N/A"
- generic "/ Bill Nr. [X] of the Bill of Quantities contains items relating to MiC works [for Option B]. For milestone payment for MiC works Reference SDEVs memo ref. DEVB(PSGO) 100/1 dated 18" [ref=e106]: / Bill Nr. [X] of the Bill of Quantities contains items relating to MiC works [for Option B]. ...
- button [ref=e107] [cursor=pointer]:
- img [ref=e108]
- generic [ref=e111]:
- generic [ref=e112]:
- generic [ref=e113]:
- generic [ref=e114]: Chunk 3
- generic [ref=e115]: "Page: N/A"
- generic "and marked. Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 6 of 187 A2 (Contd) (3B) If any key person is not identified in the Contractors tender submissi" [ref=e116]: and marked. Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 6...
- button [ref=e117] [cursor=pointer]:
- img [ref=e118]
- generic [ref=e121]:
- generic [ref=e122]:
- generic [ref=e123]:
- generic [ref=e124]: Chunk 4
- generic [ref=e125]: "Page: N/A"
- generic "21 days when so requested by the Project Manager give detailed information on the estimated cost of execution in accordance with the technical proposals and the cost of execution in accordance wi" [ref=e126]: 21 days when so requested by the Project Manager give detailed information on the estimated cost ...
- button [ref=e127] [cursor=pointer]:
- img [ref=e128]
- generic [ref=e131]:
- generic [ref=e132]:
- generic [ref=e133]:
- generic [ref=e134]: Chunk 5
- generic [ref=e135]: "Page: N/A"
- generic "the Contractors duties and obligations under the contract, provided that the Contractor has imposed on the Related Person an absolute and legally binding obligation to refrain from disclosing th" [ref=e136]: the Contractors duties and obligations under the contract, provided that the Contractor has impo...
- button [ref=e137] [cursor=pointer]:
- img [ref=e138]
- generic [ref=e141]:
- generic [ref=e142]:
- generic [ref=e143]:
- generic [ref=e144]: Chunk 6
- generic [ref=e145]: "Page: N/A"
- generic "contract. A Data Owner may, from time to time or prior to provision of any personal data, require the Contractor to demonstrate that adequate measures are in place to ensure compliance with the" [ref=e146]: contract. A Data Owner may, from time to time or prior to provision of any personal data, requir...
- button [ref=e147] [cursor=pointer]:
- img [ref=e148]
- generic [ref=e151]:
- generic [ref=e152]:
- generic [ref=e153]:
- generic [ref=e154]: Chunk 7
- generic [ref=e155]: "Page: N/A"
- generic "inform the Contractor. Disclosures shall not be made to the said Committee before expiry of the first 6 months from the date of the settlement agreement, arbitration award or, as the case may be," [ref=e156]: inform the Contractor. Disclosures shall not be made to the said Committee before expiry of the f...
- button [ref=e157] [cursor=pointer]:
- img [ref=e158]
- generic [ref=e161]:
- generic [ref=e162]:
- generic [ref=e163]:
- generic [ref=e164]: Chunk 8
- generic [ref=e165]: "Page: N/A"
- generic "contingencies for the purpose of internal administration of the Client under the Stores and Procurement Regulations only. The forecast total of the Prices is included for tender evaluation purpos" [ref=e166]: contingencies for the purpose of internal administration of the Client under the Stores and Procu...
- button [ref=e167] [cursor=pointer]:
- img [ref=e168]
- generic [ref=e171]:
- generic [ref=e172]:
- generic [ref=e173]:
- generic [ref=e174]: Chunk 9
- generic [ref=e175]: "Page: N/A"
- generic "if any) provided in the ETPI will not be disclosed to such third party. Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 18 of 187 (6) The ETPI and anything stat" [ref=e176]: if any) provided in the ETPI will not be disclosed to such third party. Marginal Notes Guidelines ...
- button [ref=e177] [cursor=pointer]:
- img [ref=e178]
- generic [ref=e181]:
- generic [ref=e182]:
- generic [ref=e183]:
- generic [ref=e184]: Chunk 10
- generic [ref=e185]: "Page: N/A"
- generic "the terms of its appointment by the Client/under the internal rules of the Client to obtain confirmation of no objection from the Client and, in the event of an objection, to act in accordance w" [ref=e186]: the terms of its appointment by the Client/under the internal rules of the Client to obtain conf...
- button [ref=e187] [cursor=pointer]:
- img [ref=e188]
- generic [ref=e191]:
- generic [ref=e192]:
- generic [ref=e193]:
- generic [ref=e194]: Chunk 11
- generic [ref=e195]: "Page: N/A"
- generic "interfacing problems or programme slippages of associated works outside the scope of the contract. (3) The Contractor shall, in any subcontract or other contract made by it in connection with" [ref=e196]: interfacing problems or programme slippages of associated works outside the scope of the contract....
- button [ref=e197] [cursor=pointer]:
- img [ref=e198]
- generic [ref=e201]:
- generic [ref=e202]:
- generic [ref=e203]:
- generic [ref=e204]: Chunk 12
- generic [ref=e205]: "Page: N/A"
- generic "Section Subject to Excision; * Delete as appropriate. (d) the contract shall thereafter be construed in every way as if the work within the Section Subject to Excision had not at any time fo" [ref=e206]: Section Subject to Excision; * Delete as appropriate. (d) the contract shall thereafter be c...
- button [ref=e207] [cursor=pointer]:
- img [ref=e208]
- generic [ref=e211]:
- generic [ref=e212]:
- generic [ref=e213]:
- generic [ref=e214]: Chunk 13
- generic [ref=e215]: "Page: N/A"
- generic "from tendering (whether by way of mandatory or voluntary suspension) in respect of the work in the relevant Group, Category and, where appropriate, Class and status. In relation to any part of th" [ref=e216]: from tendering (whether by way of mandatory or voluntary suspension) in respect of the work in th...
- button [ref=e217] [cursor=pointer]:
- img [ref=e218]
- generic [ref=e221]:
- generic [ref=e222]:
- generic [ref=e223]:
- generic [ref=e224]: Chunk 14
- generic [ref=e225]: "Page: N/A"
- generic "Notwithstanding any other provisions in the contract, compliance with sub-clause (2)(a) of this Clause shall be a condition precedent to the Contractor's entitlement to any payment, or any furthe" [ref=e226]: Notwithstanding any other provisions in the contract, compliance with sub-clause (2)(a) of this C...
- button [ref=e227] [cursor=pointer]:
- img [ref=e228]
- generic [ref=e231]:
- generic [ref=e232]:
- generic [ref=e233]:
- generic [ref=e234]: Chunk 15
- generic [ref=e235]: "Page: N/A"
- generic "Manager. (5) Subject to the provisions of other additional conditions of contract , the Contractor shall ensure that its Subcontractors shall not subcontract the whole of the works subcontr" [ref=e236]: Manager. (5) Subject to the provisions of other additional conditions of contract , the Cont...
- button [ref=e237] [cursor=pointer]:
- img [ref=e238]
- generic [ref=e241]:
- generic [ref=e242]:
- generic [ref=e243]:
- generic [ref=e244]: Chunk 16
- generic [ref=e245]: "Page: N/A"
- generic "4.2024. Modified from SCC at Annex A of the above memo (which supersedes SCC 57) Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 34 of 187 C6 (Contd)" [ref=e246]: 4.2024. Modified from SCC at Annex A of the above memo (which supersedes SCC 57) Marginal N...
- button [ref=e247] [cursor=pointer]:
- img [ref=e248]
- generic [ref=e251]:
- generic [ref=e252]:
- generic [ref=e253]:
- generic [ref=e254]: Chunk 17
- generic [ref=e255]: "Page: N/A"
- generic "), the subcontractor (irrespective of any tier) has been admitted into Group 2, before the commencement of the work under the relevant subcontract The Contractor shall also ensure that a subcontra" [ref=e256]: ), the subcontractor (irrespective of any tier) has been admitted into Group 2, before the commenc...
- button [ref=e257] [cursor=pointer]:
- img [ref=e258]
- generic [ref=e261]:
- generic [ref=e262]:
- generic [ref=e263]:
- generic [ref=e264]: Chunk 18
- generic [ref=e265]: "Page: N/A"
- generic "tier of subcontracting means the contracts between the Contractor and its subcontractors. The second tier means the subcontracts between any of the subcontractors of the first tier and their sub" [ref=e266]: tier of subcontracting means the contracts between the Contractor and its subcontractors. The se...
- button [ref=e267] [cursor=pointer]:
- img [ref=e268]
- generic [ref=e271]:
- generic [ref=e272]:
- generic [ref=e273]:
- generic [ref=e274]: Chunk 19
- generic [ref=e275]: "Page: N/A"
- 'generic "in the Factories and Industrial Undertakings (Confined Spaces) Regulation (Cap. 59AE). Internal Notes: * Delete as appropriate. 1. Insert appropriate references which refer to Clause C" [ref=e276]': in the Factories and Industrial Undertakings (Confined Spaces) Regulation (Cap. 59AE). Int...
- button [ref=e277] [cursor=pointer]:
- img [ref=e278]
- generic [ref=e281]:
- generic [ref=e282]:
- generic [ref=e283]:
- generic [ref=e284]: Chunk 20
- generic [ref=e285]: "Page: N/A"
- generic "Library of Standard ACC for NEC4 ECC (24.4.2024) Page 40 of 187 C8 (Contd) (4) The Contractor shall comply with and shall ensure that all subcontractors engaged by the Contractor shall comply wi" [ref=e286]: Library of Standard ACC for NEC4 ECC (24.4.2024) Page 40 of 187 C8 (Contd) (4) The Contractor sh...
- button [ref=e287] [cursor=pointer]:
- img [ref=e288]
- generic [ref=e291]:
- generic [ref=e292]:
- generic [ref=e293]:
- generic [ref=e294]: Chunk 21
- generic [ref=e295]: "Page: N/A"
- generic "of mandatory or voluntary suspension) in respect of the work in the relevant category, group and class; or (B) contractors who possess experience in the works to be subcontracted by the Contrac" [ref=e296]: of mandatory or voluntary suspension) in respect of the work in the relevant category, group and...
- button [ref=e297] [cursor=pointer]:
- img [ref=e298]
- generic [ref=e301]:
- generic [ref=e302]:
- generic [ref=e303]:
- generic [ref=e304]: Chunk 22
- generic [ref=e305]: "Page: N/A"
- generic "3 weeks if it is acceptable and shall inform the Contractor whether to select the conforming tender which does not offer the lowest tender price. Marginal Notes Guidelines Library of Standard" [ref=e306]: 3 weeks if it is acceptable and shall inform the Contractor whether to select the conforming tend...
- button [ref=e307] [cursor=pointer]:
- img [ref=e308]
- generic [ref=e311]:
- generic [ref=e312]:
- generic [ref=e313]:
- generic [ref=e314]: Chunk 23
- generic [ref=e315]: "Page: N/A"
- generic "tender may cause the tender to be disqualified. Subject to acceptance by the Project Manager , the Contractor may approach a tenderer in seeking clarification on the purpose or meaning of partic" [ref=e316]: tender may cause the tender to be disqualified. Subject to acceptance by the Project Manager , t...
- button [ref=e317] [cursor=pointer]:
- img [ref=e318]
- generic [ref=e321]:
- generic [ref=e322]:
- generic [ref=e323]:
- generic [ref=e324]: Chunk 24
- generic [ref=e325]: "Page: N/A"
- generic "Manager, shall be deducted from the total of the Prices. Nevertheless, the total of the Prices shall not in any case be increased due to a change of such Subcontractor pursuant to this sub-claus" [ref=e326]: Manager, shall be deducted from the total of the Prices. Nevertheless, the total of the Prices s...
- button [ref=e327] [cursor=pointer]:
- img [ref=e328]
- generic [ref=e331]:
- generic [ref=e332]:
- generic [ref=e333]:
- generic [ref=e334]: Chunk 25
- generic [ref=e335]: "Page: N/A"
- generic "or more of whose directors is in common with one or more of the directors of the Contractor. “associated person” or “associated persons” in relation to the Contractor means (i) any person who ha" [ref=e336]: or more of whose directors is in common with one or more of the directors of the Contractor. “as...
- button [ref=e337] [cursor=pointer]:
- img [ref=e338]
- generic [ref=e341]:
- generic [ref=e342]:
- generic [ref=e343]:
- generic [ref=e344]: Chunk 26
- generic [ref=e345]: "Page: N/A"
- generic "or not the tenderer has attended the post-tender interview. Tenderers invited for the post -tender interviews shall then be invited to submit a revised tender price. In case the tender documents" [ref=e346]: or not the tenderer has attended the post-tender interview. Tenderers invited for the post -tender...
- button [ref=e347] [cursor=pointer]:
- img [ref=e348]
- generic [ref=e351]:
- generic [ref=e352]:
- generic [ref=e353]:
- generic [ref=e354]: Chunk 27
- generic [ref=e355]: "Page: N/A"
- generic "Options A and B Modified from GCT 26 (b) Further to paragraph (a) of this sub -clause, the tenderer shall not fix the amount of the tender price or any part thereof by arrangement with any" [ref=e356]: Options A and B Modified from GCT 26 (b) Further to paragraph (a) of this sub -clause, the ...
- button [ref=e357] [cursor=pointer]:
- img [ref=e358]
- generic [ref=e361]:
- generic [ref=e362]:
- generic [ref=e363]:
- generic [ref=e364]: Chunk 28
- generic [ref=e365]: "Page: N/A"
- generic "Page 56 of 187 [I/We]1 shall indemnify and keep indemnified the Contractor against all losses, damages, costs or expenses arising out of or in relation to any breach of any of the representatio" [ref=e366]: Page 56 of 187 [I/We]1 shall indemnify and keep indemnified the Contractor against all losses, ...
- button [ref=e367] [cursor=pointer]:
- img [ref=e368]
- generic [ref=e371]:
- generic [ref=e372]:
- generic [ref=e373]:
- generic [ref=e374]: Chunk 29
- generic [ref=e375]: "Page: N/A"
- generic "ACC for NEC4 ECC (24.4.2024) Page 59 of 187 C11 (Contd) (2) The Contractor shall ensure that the tender prices and rates of the subcontracts are competitively tendered or open market prices or" [ref=e376]: ACC for NEC4 ECC (24.4.2024) Page 59 of 187 C11 (Contd) (2) The Contractor shall ensure that th...
- button [ref=e377] [cursor=pointer]:
- img [ref=e378]
- generic [ref=e381]:
- generic [ref=e382]:
- generic [ref=e383]:
- generic [ref=e384]: Chunk 30
- generic [ref=e385]: "Page: N/A"
- generic "in accordance with the procedures set out in this Clause C11. Any cost saving as a result of any change of such supplier, which is calculated as the difference between the lump sum prices for s" [ref=e386]: in accordance with the procedures set out in this Clause C11. Any cost saving as a result of an...
- button [ref=e387] [cursor=pointer]:
- img [ref=e388]
- generic [ref=e391]:
- generic [ref=e392]:
- generic [ref=e393]:
- generic [ref=e394]: Chunk 31
- generic [ref=e395]: "Page: N/A"
- generic "to update as appropriate.] Contractors Management Team SDEVs memo ref (027RU-01- 3) in DEVB(W) 510/17/01 dated 16.7.2010 Modified from SCC68 & 68A Project Offices to review to inclu" [ref=e396]: to update as appropriate.] Contractors Management Team SDEVs memo ref (027RU-01- 3) in DEVB(...
- button [ref=e397] [cursor=pointer]:
- img [ref=e398]
- generic [ref=e401]:
- generic [ref=e402]:
- generic [ref=e403]:
- generic [ref=e404]: Chunk 32
- generic [ref=e405]: "Page: N/A"
- generic "pay all licences, levies, premiums or other fees required to be given or paid by reason of any enactment or any regulations or bye -laws of any local or other duly constituted authority in relati" [ref=e406]: pay all licences, levies, premiums or other fees required to be given or paid by reason of any en...
- button [ref=e407] [cursor=pointer]:
- img [ref=e408]
- generic [ref=e411]:
- generic [ref=e412]:
- generic [ref=e413]:
- generic [ref=e414]: Chunk 33
- generic [ref=e415]: "Page: N/A"
- generic "shall submit within 14 days of the Contract Date three copies of a draft Safety Plan to the Supervisor. requirement (3) Within 7 days from the submission of the draft Safety Plan, the Contract" [ref=e416]: shall submit within 14 days of the Contract Date three copies of a draft Safety Plan to the Super...
- button [ref=e417] [cursor=pointer]:
- img [ref=e418]
- generic [ref=e421]:
- generic [ref=e422]:
- generic [ref=e423]:
- generic [ref=e424]: Chunk 34
- generic [ref=e425]: "Page: N/A"
- generic "works and Establishment Works. Establishment Works Modified from SCC16 For contracts with Landscape Works Sub-clause (2) is used when the Establishment Works is not required to complet" [ref=e426]: works and Establishment Works. Establishment Works Modified from SCC16 For contracts with La...
- button [ref=e427] [cursor=pointer]:
- img [ref=e428]
- generic [ref=e431]:
- generic [ref=e432]:
- generic [ref=e433]:
- generic [ref=e434]: Chunk 35
- generic [ref=e435]: "Page: N/A"
- generic "the Scope]. Any instruction given by the Project Manager due to such cleaning and tidying up work performed outside the boundaries of the site shall not constitute a compensation event. (" [ref=e436]: the Scope]. Any instruction given by the Project Manager due to such cleaning and tidying up work...
- button [ref=e437] [cursor=pointer]:
- img [ref=e438]
- generic [ref=e441]:
- generic [ref=e442]:
- generic [ref=e443]:
- generic [ref=e444]: Chunk 36
- generic [ref=e445]: "Page: N/A"
- generic "submit to the Project Manager for acceptance within 30 days of the Contract Date a quality system for production and supply of structural concrete for incorporation into the works. The quality" [ref=e446]: submit to the Project Manager for acceptance within 30 days of the Contract Date a quality syste...
- button [ref=e447] [cursor=pointer]:
- img [ref=e448]
- generic [ref=e451]:
- generic [ref=e452]:
- generic [ref=e453]:
- generic [ref=e454]: Chunk 37
- generic [ref=e455]: "Page: N/A"
- generic "187 D14 (1) If the Contractor or any of its agents or employees shall be found to have offered or given any advantage, gratuity, bonus, discount, bribe or loan of any sort to any agent or emplo" [ref=e456]: 187 D14 (1) If the Contractor or any of its agents or employees shall be found to have offered ...
- button [ref=e457] [cursor=pointer]:
- img [ref=e458]
- generic [ref=e461]:
- generic [ref=e462]:
- generic [ref=e463]:
- generic [ref=e464]: Chunk 38
- generic [ref=e465]: "Page: N/A"
- generic "of Standard ACC for NEC4 ECC (24.4.2024) Page 82 of 187 D17 The Contractor acknowledges that it has been reminded that dishonesty, theft and corruption on its part or those of its employees," [ref=e466]: of Standard ACC for NEC4 ECC (24.4.2024) Page 82 of 187 D17 The Contractor acknowledges that i...
- button [ref=e467] [cursor=pointer]:
- img [ref=e468]
- generic [ref=e471]:
- generic [ref=e472]:
- generic [ref=e473]:
- generic [ref=e474]: Chunk 39
- generic [ref=e475]: "Page: N/A"
- generic "D18 (Contd) (b) Where excavation in land other than Street Maintained by the Highways Department that requires Excavation Permit under the Ordinance is required to Provide the Works, the Cont" [ref=e476]: D18 (Contd) (b) Where excavation in land other than Street Maintained by the Highways Departm...
- button [ref=e477] [cursor=pointer]:
- img [ref=e478]
- generic [ref=e481]:
- generic [ref=e482]:
- generic [ref=e483]:
- generic [ref=e484]: Chunk 40
- generic [ref=e485]: "Page: N/A"
- generic "the Contractors part under the contract; D18 (Contd) (b) the Project Manager shall notify the Contractor when an Excavation Permit has b een obtained. If during the course of the work" [ref=e486]: the Contractors part under the contract; D18 (Contd) (b) the Project Manager shall not...
- button [ref=e487] [cursor=pointer]:
- img [ref=e488]
- generic [ref=e491]:
- generic [ref=e492]:
- generic [ref=e493]:
- generic [ref=e494]: Chunk 41
- generic [ref=e495]: "Page: N/A"
- generic "return such share to the Contractor. For the avoidance of doubt, the opening up for inspection of any work covered up or put out of view, or the testing of materials or workmanship not req" [ref=e496]: return such share to the Contractor. For the avoidance of doubt, the opening up for inspec...
- button [ref=e497] [cursor=pointer]:
- img [ref=e498]
- generic [ref=e501]:
- generic [ref=e502]:
- generic [ref=e503]:
- generic [ref=e504]: Chunk 42
- generic [ref=e505]: "Page: N/A"
- generic "respect of application for the Excavation Permit) if the Client chooses to apply for an Excavation Permit of its own volition whether before, on, or after the Contract Date. 1 Inse" [ref=e506]: respect of application for the Excavation Permit) if the Client chooses to apply for an Excavation ...
- button [ref=e507] [cursor=pointer]:
- img [ref=e508]
- generic [ref=e511]:
- generic [ref=e512]:
- generic [ref=e513]:
- generic [ref=e514]: Chunk 43
- generic [ref=e515]: "Page: N/A"
- generic "that the total sum withheld by the Client on the ground of failure to comply with any written request referred to in sub -clause ( 2) of this Clause shall not exceed an amount equal to the market" [ref=e516]: that the total sum withheld by the Client on the ground of failure to comply with any written req...
- button [ref=e517] [cursor=pointer]:
- img [ref=e518]
- generic [ref=e521]:
- generic [ref=e522]:
- generic [ref=e523]:
- generic [ref=e524]: Chunk 44
- generic [ref=e525]: "Page: N/A"
- generic "work as part of the EMP to the Supervisor for approval prior to the commencement of the demolition on the Site. The Contractor shall include in the method statement the sequence of demolition and" [ref=e526]: work as part of the EMP to the Supervisor for approval prior to the commencement of the demolitio...
- button [ref=e527] [cursor=pointer]:
- img [ref=e528]
- generic [ref=e531]:
- generic [ref=e532]:
- generic [ref=e533]:
- generic [ref=e534]: Chunk 45
- generic [ref=e535]: "Page: N/A"
- generic "joint venture it shall within fourteen (14) days of the Contract Date provide to the Client a joint venture guarantee in the form set out in Appendix [insert the relevant appendix to the tender d" [ref=e536]: joint venture it shall within fourteen (14) days of the Contract Date provide to the Client a joi...
- button [ref=e537] [cursor=pointer]:
- img [ref=e538]
- generic [ref=e541]:
- generic [ref=e542]:
- generic [ref=e543]:
- generic [ref=e544]: Chunk 46
- generic [ref=e545]: "Page: N/A"
- generic "Clause as “design of the site uniform”) in accordance with Clause [insert appropriate clause no] of the Particular Specification does not and will not infringe any Intellectual Property Rights" [ref=e546]: Clause as “design of the site uniform”) in accordance with Clause [insert appropriate clause no] ...
- button [ref=e547] [cursor=pointer]:
- img [ref=e548]
- generic [ref=e551]:
- generic [ref=e552]:
- generic [ref=e553]:
- generic [ref=e554]: Chunk 47
- generic [ref=e555]: "Page: N/A"
- generic "inal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 101 of 187 D26 (1) This additional condition of contract shall apply to works within the Railway Protection Area a" [ref=e556]: inal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 101 of 187 D26 (...
- button [ref=e557] [cursor=pointer]:
- img [ref=e558]
- generic [ref=e561]:
- generic [ref=e562]:
- generic [ref=e563]:
- generic [ref=e564]: Chunk 48
- generic [ref=e565]: "Page: N/A"
- generic "or shall not prevent and shall permit MTRCL or MTRCLs workers or contractors to carry out the remedial work s or other work s or repairs required by the direct instruction. (c) If in the op" [ref=e566]: or shall not prevent and shall permit MTRCL or MTRCLs workers or contractors to carry out the re...
- button [ref=e567] [cursor=pointer]:
- img [ref=e568]
- generic [ref=e571]:
- generic [ref=e572]:
- generic [ref=e573]:
- generic [ref=e574]: Chunk 49
- generic [ref=e575]: "Page: N/A"
- generic "agreed by MTRCL. Should the Contractor in the opinion of the Project Manager or MTRCL not make sufficient or adequate arrangements (including the provision of standby plant) for completing the" [ref=e576]: agreed by MTRCL. Should the Contractor in the opinion of the Project Manager or MTRCL not make ...
- button [ref=e577] [cursor=pointer]:
- img [ref=e578]
- generic [ref=e581]:
- generic [ref=e582]:
- generic [ref=e583]:
- generic [ref=e584]: Chunk 50
- generic [ref=e585]: "Page: N/A"
- generic "the District Lands Officer shall be informed by the Project Manager , and representative or representatives of the District Lands Office will be present at the negotiations and any payment in set" [ref=e586]: the District Lands Officer shall be informed by the Project Manager , and representative or repre...
- button [ref=e587] [cursor=pointer]:
- img [ref=e588]
- generic [ref=e591]:
- generic [ref=e592]:
- generic [ref=e593]:
- generic [ref=e594]: Chunk 51
- generic [ref=e595]: "Page: N/A"
- generic "previously, is required, the Contractor shall revise and re - submit the relevant Monthly Report to the Project Manager for correction and acceptance. Any amounts for the performance - tied payme" [ref=e596]: previously, is required, the Contractor shall revise and re - submit the relevant Monthly Report t...
- button [ref=e597] [cursor=pointer]:
- img [ref=e598]
- generic [ref=e601]:
- generic [ref=e602]:
- generic [ref=e603]:
- generic [ref=e604]: Chunk 52
- generic [ref=e605]: "Page: N/A"
- generic "bar product supplied by yards not on the List for any part of works. Should the Contractor opt to use prefabricated rebar products for any part or parts of works, it shall engage an approved pref" [ref=e606]: bar product supplied by yards not on the List for any part of works. Should the Contractor opt to...
- button [ref=e607] [cursor=pointer]:
- img [ref=e608]
- generic [ref=e611]:
- generic [ref=e612]:
- generic [ref=e613]:
- generic [ref=e614]: Chunk 53
- generic [ref=e615]: "Page: N/A"
- generic "Specification for Civil Engineering Works 2006 Edition Clauses 15.12 and 15.17(a), (e) and (f); and (b) Testing General Specification for Civil Engineering Works 2006 Edition Clauses 15.30, 15.3" [ref=e616]: Specification for Civil Engineering Works 2006 Edition Clauses 15.12 and 15.17(a), (e) and (f); a...
- button [ref=e617] [cursor=pointer]:
- img [ref=e618]
- generic [ref=e621]:
- generic [ref=e622]:
- generic [ref=e623]:
- generic [ref=e624]: Chunk 54
- generic [ref=e625]: "Page: N/A"
- generic "section 2 of the Air Pollution Control (Non -road Mobile Machinery)(Emission) Regulation; (b) “exempted” has the meaning given by section 2 of the Air Pollution Control (Non -road Mobile Machine" [ref=e626]: section 2 of the Air Pollution Control (Non -road Mobile Machinery)(Emission) Regulation; (b) “e...
- button [ref=e627] [cursor=pointer]:
- img [ref=e628]
- generic [ref=e631]:
- generic [ref=e632]:
- generic [ref=e633]:
- generic [ref=e634]: Chunk 55
- generic [ref=e635]: "Page: N/A"
- generic "any reason, all Special Payments made by the Client for Relevant Imported Items that are not yet delivered by the Contractor to the Site on the date of expiry or termination of the contract shall" [ref=e636]: any reason, all Special Payments made by the Client for Relevant Imported Items that are not yet ...
- button [ref=e637] [cursor=pointer]:
- img [ref=e638]
- generic [ref=e641]:
- generic [ref=e642]:
- generic [ref=e643]:
- generic [ref=e644]: Chunk 56
- generic [ref=e645]: "Page: N/A"
- generic "or, and whether the person claims for compensation. (6) The Contractor shall make necessary arrangements to ensure that all subcontractors report all accidents on the Site involving their e" [ref=e646]: or, and whether the person claims for compensation. (6) The Contractor shall make necessary...
- button [ref=e647] [cursor=pointer]:
- img [ref=e648]
- generic [ref=e651]:
- generic [ref=e652]:
- generic [ref=e653]:
- generic [ref=e654]: Chunk 57
- generic [ref=e655]: "Page: N/A"
- generic "modules approved Stage 3 8% Preparatory works necessary for commencement of fabrication of all MiC modules completed Stage 4 35% MiC modules completed off -Site (before delivery t" [ref=e656]: modules approved Stage 3 8% Preparatory works necessary for commencement of fabrication ...
- button [ref=e657] [cursor=pointer]:
- img [ref=e658]
- generic [ref=e661]:
- generic [ref=e662]:
- generic [ref=e663]:
- generic [ref=e664]: Chunk 58
- generic [ref=e665]: "Page: N/A"
- 'generic "have been clearly and visibly marked, individually or as a set, as follows: “Property of the Government of the Hong Kong Special Administrative Region. For use in [contract no. and title]. D" [ref=e666]': "have been clearly and visibly marked, individually or as a set, as follows: “Property of the G..."
- button [ref=e667] [cursor=pointer]:
- img [ref=e668]
- generic [ref=e671]:
- generic [ref=e672]:
- generic [ref=e673]:
- generic [ref=e674]: Chunk 59
- generic [ref=e675]: "Page: N/A"
- generic "Others and compensation and costs payable to Others which arise from or in connection with the Relevant Works and any loss of or damage to the Relevant Works are the Contractors liabilities, and" [ref=e676]: Others and compensation and costs payable to Others which arise from or in connection with the Rel...
- button [ref=e677] [cursor=pointer]:
- img [ref=e678]
- generic [ref=e681]:
- generic [ref=e682]:
- generic [ref=e683]:
- generic [ref=e684]: Chunk 60
- generic [ref=e685]: "Page: N/A"
- generic "in relation to such Stage in Column 2 of Table B in this sub -clause to the total of the Prices of MiMEP works without mock-up in *Schedule Nr. [Y] of the Activity Schedule [for Option A] /B" [ref=e686]: in relation to such Stage in Column 2 of Table B in this sub -clause to the total of the Prices o...
- button [ref=e687] [cursor=pointer]:
- img [ref=e688]
- generic [ref=e691]:
- generic [ref=e692]:
- generic [ref=e693]:
- generic [ref=e694]: Chunk 61
- generic [ref=e695]: "Page: N/A"
- generic "of MiMEP works (whether with or without mock-up), if the Contractor wishes to apply for interim payment before the MiMEP works are delivered to the Site, the Contractor shall comply with the requ" [ref=e696]: of MiMEP works (whether with or without mock-up), if the Contractor wishes to apply for interim p...
- button [ref=e697] [cursor=pointer]:
- img [ref=e698]
- generic [ref=e701]:
- generic [ref=e702]:
- generic [ref=e703]:
- generic [ref=e704]: Chunk 62
- generic [ref=e705]: "Page: N/A"
- generic "contractor for the Relevant MiMEP Works in the form annexed at Appendix [ insert appropriate reference ] to these additional conditions of contract. Marginal Notes Guidelines Library of Standa" [ref=e706]: contractor for the Relevant MiMEP Works in the form annexed at Appendix [ insert appropriate refer...
- button [ref=e707] [cursor=pointer]:
- img [ref=e708]
- generic [ref=e711]:
- generic [ref=e712]:
- generic [ref=e713]:
- generic [ref=e714]: Chunk 63
- generic [ref=e715]: "Page: N/A"
- generic "2024) Page 133 of 187 Section E - Labour E1 (1) The Contractor shall make its own arrangements in regard to the provision of such labour, skilled and unskilled, as may be required to Provide th" [ref=e716]: 2024) Page 133 of 187 Section E - Labour E1 (1) The Contractor shall make its own arrangements ...
- button [ref=e717] [cursor=pointer]:
- img [ref=e718]
- generic [ref=e721]:
- generic [ref=e722]:
- generic [ref=e723]:
- generic [ref=e724]: Chunk 64
- generic [ref=e725]: "Page: N/A"
- generic "to any part thereof and in such event any person who fails to show its pass on demand to any duly authorized person may be refused admission. Passes Modified from GCC41 (2) If required by" [ref=e726]: to any part thereof and in such event any person who fails to show its pass on demand to any dul...
- button [ref=e727] [cursor=pointer]:
- img [ref=e728]
- generic [ref=e731]:
- generic [ref=e732]:
- generic [ref=e733]:
- generic [ref=e734]: Chunk 65
- generic [ref=e735]: "Page: N/A"
- generic "insurance plan with a minimum coverage of HK$1,000,000 by either extending the Contractors employee compensation insurance policy or its third party liability and all risks insurance policy. A" [ref=e736]: insurance plan with a minimum coverage of HK$1,000,000 by either extending the Contractors empl...
- button [ref=e737] [cursor=pointer]:
- img [ref=e738]
- generic [ref=e741]:
- generic [ref=e742]:
- generic [ref=e743]:
- generic [ref=e744]: Chunk 66
- generic [ref=e745]: "Page: N/A"
- generic "public holidays excluded) of the final due date for payment as prescribed under section 23 of the Employment Ordinance (Cap 57), the Client may, after the Contractor or the subcontractor, as the" [ref=e746]: public holidays excluded) of the final due date for payment as prescribed under section 23 of the...
- button [ref=e747] [cursor=pointer]:
- img [ref=e748]
- generic [ref=e751]:
- generic [ref=e752]:
- generic [ref=e753]:
- generic [ref=e754]: Chunk 67
- generic [ref=e755]: "Page: N/A"
- generic "ECMTS. (2) An ECMTS Graduate employed by a subcontractor of the Contractor within the 6-month period to work on the Site in accordance with the provisions of this Clause shall be counted towa" [ref=e756]: ECMTS. (2) An ECMTS Graduate employed by a subcontractor of the Contractor within the 6-month...
- button [ref=e757] [cursor=pointer]:
- img [ref=e758]
- generic [ref=e761]:
- generic [ref=e762]:
- generic [ref=e763]:
- generic [ref=e764]: Chunk 68
- generic [ref=e765]: "Page: N/A"
- 'generic "the minimum 12 -month employment period, unless: (i) there is no work or insufficient work under the contract that is suitable for an ECMTS Graduate; or (ii) the remaining time under the min" [ref=e766]': "the minimum 12 -month employment period, unless: (i) there is no work or insufficient work un..."
- button [ref=e767] [cursor=pointer]:
- img [ref=e768]
- generic [ref=e771]:
- generic [ref=e772]:
- generic [ref=e773]:
- generic [ref=e774]: Chunk 69
- generic [ref=e775]: "Page: N/A"
- generic "reference ] to these additional conditions of contract , issued by the Independent Checking Engineer certifying that the Contractor's Design has been independently checked and complies in all res" [ref=e776]: reference ] to these additional conditions of contract , issued by the Independent Checking Engin...
- button [ref=e777] [cursor=pointer]:
- img [ref=e778]
- generic [ref=e781]:
- generic [ref=e782]:
- generic [ref=e783]:
- generic [ref=e784]: Chunk 70
- generic [ref=e785]: "Page: N/A"
- generic "provisions of the Scope, provided that the Contractor may propose modifications to the Scope in respect of particular methods of construction or materials not included in the Scope. In such cases," [ref=e786]: provisions of the Scope, provided that the Contractor may propose modifications to the Scope in res...
- button [ref=e787] [cursor=pointer]:
- img [ref=e788]
- generic [ref=e791]:
- generic [ref=e792]:
- generic [ref=e793]:
- generic [ref=e794]: Chunk 71
- generic [ref=e795]: "Page: N/A"
- generic "Page 152 of 187 F2 (Contd) (8) On completion of the work constructed in accordance with the Contractor's Design, the Contractor shall prepare and submit to the Project Manager the as constru" [ref=e796]: Page 152 of 187 F2 (Contd) (8) On completion of the work constructed in accordance with the Co...
- button [ref=e797] [cursor=pointer]:
- img [ref=e798]
- generic [ref=e801]:
- generic [ref=e802]:
- generic [ref=e803]:
- generic [ref=e804]: Chunk 72
- generic [ref=e805]: "Page: N/A"
- generic "uant to NEC Clause 30.2, the expression \"certificate of Completion\" shall, for the purpose of this sub -clause, mean the last of such certificates. Marginal Notes Guidelines Library of Standar" [ref=e806]: uant to NEC Clause 30.2, the expression "certificate of Completion" shall, for the purpose of this...
- button [ref=e807] [cursor=pointer]:
- img [ref=e808]
- generic [ref=e811]:
- generic [ref=e812]:
- generic [ref=e813]:
- generic [ref=e814]: Chunk 73
- generic [ref=e815]: "Page: N/A"
- generic "resultant works of such design, or any machine, work, method or material or anything whatsoever required for any works provided by the Contractor, its subcontractors or the manufacturers of any" [ref=e816]: resultant works of such design, or any machine, work, method or material or anything whatsoever r...
- button [ref=e817] [cursor=pointer]:
- img [ref=e818]
- generic [ref=e821]:
- generic [ref=e822]:
- generic [ref=e823]:
- generic [ref=e824]: Chunk 74
- generic [ref=e825]: "Page: N/A"
- generic "to any part of the works submitted by the Contractor under Clause [F4]1 of these additional conditions of contract and any amplification or amendment thereto and accepted by the Client with or wi" [ref=e826]: to any part of the works submitted by the Contractor under Clause [F4]1 of these additional condi...
- button [ref=e827] [cursor=pointer]:
- img [ref=e828]
- generic [ref=e831]:
- generic [ref=e832]:
- generic [ref=e833]:
- generic [ref=e834]: Chunk 75
- generic [ref=e835]: "Page: N/A"
- generic "of Standard ACC for NEC4 ECC (24.4.2024) Page 159 of 187 F4 (Contd) F4 (Contd) (c) Subject" [ref=e836]: of Standard ACC for NEC4 ECC (24.4.2024) Page 159 of 187 F4 (Contd) ...
- button [ref=e837] [cursor=pointer]:
- img [ref=e838]
- generic [ref=e841]:
- generic [ref=e842]:
- generic [ref=e843]:
- generic [ref=e844]: Chunk 76
- generic [ref=e845]: "Page: N/A"
- generic "des ign life in net present value resulted from the Cost Savings Design. The Client shall be entitled to deduct such cost from any sums due to the Contractor under the contract and/or to recover" [ref=e846]: des ign life in net present value resulted from the Cost Savings Design. The Client shall be ent...
- button [ref=e847] [cursor=pointer]:
- img [ref=e848]
- generic [ref=e851]:
- generic [ref=e852]:
- generic [ref=e853]:
- generic [ref=e854]: Chunk 77
- generic [ref=e855]: "Page: N/A"
- generic "Library of Standard ACC for NEC4 ECC (24.4.2024) Page 162 of 187 F4 (Contd) (d) If at any time the Project Manager has substantial cause for dissatisfaction with the conduct or performance of" [ref=e856]: Library of Standard ACC for NEC4 ECC (24.4.2024) Page 162 of 187 F4 (Contd) (d) If at any time ...
- button [ref=e857] [cursor=pointer]:
- img [ref=e858]
- generic [ref=e861]:
- generic [ref=e862]:
- generic [ref=e863]:
- generic [ref=e864]: Chunk 78
- generic [ref=e865]: "Page: N/A"
- generic "Client which has been rendered abortive by any such amendments. (8) On completion of the work constructed in accordance with the Cost Savings Design, the Contractor shall prepare and submit" [ref=e866]: Client which has been rendered abortive by any such amendments. (8) On completion of the wor...
- button [ref=e867] [cursor=pointer]:
- img [ref=e868]
- generic [ref=e871]:
- generic [ref=e872]:
- generic [ref=e873]:
- generic [ref=e874]: Chunk 79
- generic [ref=e875]: "Page: N/A"
- generic "and for other purpose as stated in the Scope and the contract. In the event of different certificates of Completion having been issued for different sections or parts of the works pursuant to NEC" [ref=e876]: and for other purpose as stated in the Scope and the contract. In the event of different certific...
- button [ref=e877] [cursor=pointer]:
- img [ref=e878]
- generic [ref=e881]:
- generic [ref=e882]:
- generic [ref=e883]:
- generic [ref=e884]: Chunk 80
- generic [ref=e885]: "Page: N/A"
- generic "Library of Standard ACC for NEC4 ECC (24.4.2024) Page 167 of 187 (Contd) F4 (Contd) and the" [ref=e886]: Library of Standard ACC for NEC4 ECC (24.4.2024) Page 167 of 187 (Contd) ...
- button [ref=e887] [cursor=pointer]:
- img [ref=e888]
- generic [ref=e891]:
- generic [ref=e892]:
- generic [ref=e893]:
- generic [ref=e894]: Chunk 81
- generic [ref=e895]: "Page: N/A"
- generic "relation to the design of any part or all of the works to be carried out by or on behalf of the Contractor pursuant to the contract, except the Cost Savings Design and Temporary Works, for any o" [ref=e896]: relation to the design of any part or all of the works to be carried out by or on behalf of the C...
- button [ref=e897] [cursor=pointer]:
- img [ref=e898]
- generic [ref=e901]:
- generic [ref=e902]:
- generic [ref=e903]:
- generic [ref=e904]: Chunk 82
- generic [ref=e905]: "Page: N/A"
- generic "B) the limit of indemnity in the aggregate for all claims for the period of insurance under the insurance policy shall not be less than 2 times the minimum amount required under sub-clause (1) or" [ref=e906]: B) the limit of indemnity in the aggregate for all claims for the period of insurance under the in...
- button [ref=e907] [cursor=pointer]:
- img [ref=e908]
- generic [ref=e911]:
- generic [ref=e912]:
- generic [ref=e913]:
- generic [ref=e914]: Chunk 83
- generic [ref=e915]: "Page: N/A"
- generic "additional conditions of contract issued by the insurer or insurance broker of the insurance policy and any information relating to the insurance policy that the Client may reasonably require." [ref=e916]: additional conditions of contract issued by the insurer or insurance broker of the insurance poli...
- button [ref=e917] [cursor=pointer]:
- img [ref=e918]
- generic [ref=e921]:
- generic [ref=e922]:
- generic [ref=e923]:
- generic [ref=e924]: Chunk 84
- generic [ref=e925]: "Page: N/A"
- generic "emnity Insurance in respect of Cost Savings Design DEVB TC(W) No. 3/2014 SDEVs memo ref. (02245-01- 13) in DEVB(W)510/3 4/01 dated 6.10.2009 Modified from SCC72 (2) The Contractor sh" [ref=e926]: emnity Insurance in respect of Cost Savings Design DEVB TC(W) No. 3/2014 SDEVs memo ref. (...
- button [ref=e927] [cursor=pointer]:
- img [ref=e928]
- generic [ref=e931]:
- generic [ref=e932]:
- generic [ref=e933]:
- generic [ref=e934]: Chunk 85
- generic [ref=e935]: "Page: N/A"
- generic "required under sub -clause (1) or (2) of this Clause, as the case may be. (b) If (i) the insurance policy effected pursuant to sub-clause (1) or (2) of this Clause contains a limit of ind" [ref=e936]: required under sub -clause (1) or (2) of this Clause, as the case may be. (b) If (i) the ...
- button [ref=e937] [cursor=pointer]:
- img [ref=e938]
- generic [ref=e941]:
- generic [ref=e942]:
- generic [ref=e943]:
- generic [ref=e944]: Chunk 86
- generic [ref=e945]: "Page: N/A"
- generic "(2) of this Clause does not cover the entire period of insurance required under that sub - clause, within 7 days of professional indemnity insurance being effected upon the expiry of the earlier in" [ref=e946]: (2) of this Clause does not cover the entire period of insurance required under that sub - clause, ...
- button [ref=e947] [cursor=pointer]:
- img [ref=e948]
- generic [ref=e951]:
- generic [ref=e952]:
- generic [ref=e953]:
- generic [ref=e954]: Chunk 87
- generic [ref=e955]: "Page: N/A"
- generic "erection, use and removal of the Temporary Works, and (b) consider the ground conditions, the adequacy of foundations and support of the Temporary Works and any other factors which may affect t" [ref=e956]: erection, use and removal of the Temporary Works, and (b) consider the ground conditions, the ad...
- button [ref=e957] [cursor=pointer]:
- img [ref=e958]
- generic [ref=e961]:
- generic [ref=e962]:
- generic [ref=e963]:
- generic [ref=e964]: Chunk 88
- generic [ref=e965]: "Page: N/A"
- generic "to dismiss the independent checking engineer and the Contractor shall do so with immediate effect and not re-employing it again in connection with the works and shall replace the independent chec" [ref=e966]: to dismiss the independent checking engineer and the Contractor shall do so with immediate effect...
- button [ref=e967] [cursor=pointer]:
- img [ref=e968]
- generic [ref=e971]:
- generic [ref=e972]:
- generic [ref=e973]:
- generic [ref=e974]: Chunk 89
- generic [ref=e975]: "Page: N/A"
- generic "reason of indemnity payments made on account of any claim, loss, damage, liability, cost or expense paid or payable under the insurance policy until the total amount of indemnity payable by the" [ref=e976]: reason of indemnity payments made on account of any claim, loss, damage, liability, cost or expe...
- button [ref=e977] [cursor=pointer]:
- img [ref=e978]
- generic [ref=e981]:
- generic [ref=e982]:
- generic [ref=e983]:
- generic [ref=e984]: Chunk 90
- generic [ref=e985]: "Page: N/A"
- generic "of the full insurance policy effected pursuant to sub -clause (1) of this Clause for the acceptance of the Client unless the Contractor can demonstrate to the satisfaction of the Client that it" [ref=e986]: of the full insurance policy effected pursuant to sub -clause (1) of this Clause for the acceptan...
- button [ref=e987] [cursor=pointer]:
- img [ref=e988]
- generic [ref=e991]:
- generic [ref=e992]:
- generic [ref=e993]:
- generic [ref=e994]: Chunk 91
- generic [ref=e995]: "Page: N/A"
- generic "is developed, adopted, produced or used by itself, its subcontractors or the manufacturers of any proprietary product or system selected by it to Provide the Works and/or in the performance of th" [ref=e996]: is developed, adopted, produced or used by itself, its subcontractors or the manufacturers of any...
- button [ref=e997] [cursor=pointer]:
- img [ref=e998]
- generic [ref=e1001]:
- generic [ref=e1002]:
- generic [ref=e1003]:
- generic [ref=e1004]: Chunk 92
- generic [ref=e1005]: "Page: N/A"
- generic "( including the Contractor's Design and Cost Savings Design) or any part(s) thereof or the resultant works of such design or any machine, work, method or material or anything whatsoever required" [ref=e1006]: ( including the Contractor's Design and Cost Savings Design) or any part(s) thereof or the result...
- button [ref=e1007] [cursor=pointer]:
- img [ref=e1008]
- generic [ref=e1011]:
- generic [ref=e1012]:
- generic [ref=e1013]:
- generic [ref=e1014]: Chunk 93
- generic [ref=e1015]: "Page: N/A"
- generic "icator arising from an adjudication under the SOP Provisions , or a rbitration award. If the Project Manager shall fail to give such decision for a period of 28 days after being requested to do so" [ref=e1016]: icator arising from an adjudication under the SOP Provisions , or a rbitration award. If the Projec...
- button [ref=e1017] [cursor=pointer]:
- img [ref=e1018]
- generic [ref=e1021]:
- generic [ref=e1022]:
- generic [ref=e1023]:
- generic [ref=e1024]: Chunk 94
- generic [ref=e1025]: "Page: N/A"
- 'generic "Page 193 of 187 G1B (Contd) (3) Any reference to arbitration shall be made within 90 days of: (a) the receipt of a request for mediation and subsequently the recipient of such request having" [ref=e1026]': "Page 193 of 187 G1B (Contd) (3) Any reference to arbitration shall be made within 90 days of: ..."
- button [ref=e1027] [cursor=pointer]:
- img [ref=e1028]
- generic [ref=e1031]:
- generic [ref=e1032]:
- generic [ref=e1033]:
- generic [ref=e1034]: Chunk 95
- generic [ref=e1035]: "Page: N/A"
- generic ") of this sub - clause, the Domestic Arbitration Rules (2014) of the Hong Kong International Arbitration Centre (the Arbitration Rules) shall apply to any arbitration instituted in accordance with" [ref=e1036]: ) of this sub - clause, the Domestic Arbitration Rules (2014) of the Hong Kong International Arbitr...
- button [ref=e1037] [cursor=pointer]:
- img [ref=e1038]
- generic [ref=e1041]:
- generic [ref=e1042]:
- generic [ref=e1043]:
- generic [ref=e1044]: Chunk 96
- generic [ref=e1045]: "Page: N/A"
- generic "ref. DEVB(W) 510/83/03 dated 11.2.2021 Marginal Notes Guidelines Library of Standard ACC for NEC4 ECC (24.4.2024) Page 198 of 187 Section P Security of Payment P1 (1) The contract incorpo" [ref=e1046]: ref. DEVB(W) 510/83/03 dated 11.2.2021 Marginal Notes Guidelines Library of Standard ACC for ...
- button [ref=e1047] [cursor=pointer]:
- img [ref=e1048]

View File

@ -0,0 +1,17 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e9]:
- generic [ref=e12]:
- img [ref=e13]
- generic [ref=e15]: Video upload coming in Phase 2
- generic [ref=e17]:
- textbox "Ask a question about your documents..." [ref=e18]
- button "Submit" [disabled] [ref=e20]
- generic [ref=e23]:
- img [ref=e24]
- generic [ref=e26]: Ask a question to see the answer here.

View File

@ -0,0 +1,17 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e9]:
- generic [ref=e12]:
- img [ref=e13]
- generic [ref=e15]: Video upload coming in Phase 2
- generic [ref=e17]:
- textbox "Ask a question about your documents..." [ref=e18]
- button "Submit" [disabled] [ref=e20]
- generic [ref=e23]:
- img [ref=e24]
- generic [ref=e26]: Ask a question to see the answer here.

View File

@ -0,0 +1,19 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e10]:
- generic [ref=e13]:
- generic [ref=e16]:
- img [ref=e17]
- generic [ref=e19]: Video upload coming in Phase 2
- generic [ref=e21]:
- textbox "Ask a question about your documents..." [ref=e22]
- button "Submit" [disabled] [ref=e24]
- separator [ref=e25]
- generic [ref=e31]:
- img [ref=e32]
- generic [ref=e34]: Ask a question to see the answer here.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,19 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e10]:
- generic [ref=e13]:
- generic [ref=e16]:
- img [ref=e17]
- generic [ref=e19]: Video upload coming in Phase 2
- generic [ref=e21]:
- textbox "Ask a question about your documents..." [ref=e22]
- button "Submit" [disabled] [ref=e24]
- separator [ref=e25]
- generic [ref=e31]:
- img [ref=e32]
- generic [ref=e34]: Ask a question to see the answer here.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,19 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e10]:
- generic [ref=e13]:
- generic [ref=e16]:
- img [ref=e17]
- generic [ref=e19]: Video upload coming in Phase 2
- generic [ref=e21]:
- textbox "Ask a question about your documents..." [ref=e22]
- button "Submit" [disabled] [ref=e24]
- separator [ref=e25]
- generic [ref=e31]:
- img [ref=e32]
- generic [ref=e34]: Ask a question to see the answer here.

View File

@ -0,0 +1,19 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e10]:
- generic [ref=e13]:
- generic [ref=e16]:
- img [ref=e17]
- generic [ref=e19]: Video upload coming in Phase 2
- generic [ref=e21]:
- textbox "Ask a question about your documents..." [ref=e22]
- button "Submit" [disabled] [ref=e24]
- separator [ref=e25]
- generic [ref=e31]:
- img [ref=e32]
- generic [ref=e34]: Ask a question to see the answer here.

View File

@ -0,0 +1,19 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e10]:
- generic [ref=e13]:
- generic [ref=e16]:
- img [ref=e17]
- generic [ref=e19]: Video upload coming in Phase 2
- generic [ref=e21]:
- textbox "Ask a question about your documents..." [ref=e22]
- button "Submit" [disabled] [ref=e24]
- separator [ref=e25]
- generic [ref=e31]:
- img [ref=e32]
- generic [ref=e34]: Ask a question to see the answer here.

View File

@ -0,0 +1,19 @@
- generic [ref=e3]:
- navigation [ref=e4]:
- generic [ref=e5]:
- link "LTT" [ref=e6] [cursor=pointer]:
- /url: /
- link "RAG Database" [ref=e7] [cursor=pointer]:
- /url: /rag-database
- generic [ref=e10]:
- generic [ref=e13]:
- generic [ref=e16]:
- img [ref=e17]
- generic [ref=e19]: Video upload coming in Phase 2
- generic [ref=e21]:
- textbox "Ask a question about your documents..." [ref=e22]
- button "Submit" [disabled] [ref=e24]
- separator [ref=e25]
- generic [ref=e31]:
- img [ref=e32]
- generic [ref=e34]: Ask a question to see the answer here.

View File

@ -1,4 +1,4 @@
from typing import List
from typing import List, Literal, Union
from pydantic import BaseModel
@ -13,3 +13,33 @@ class QueryResponse(BaseModel):
extracted_questions: List[str]
answer: str
sources: List[SourceMetadata]
class DecomposedEvent(BaseModel):
phase: Literal["decomposed"]
extracted_questions: List[str]
class RetrievingEvent(BaseModel):
phase: Literal["retrieving"]
class FilteringEvent(BaseModel):
phase: Literal["filtering"]
class CompletedEvent(BaseModel):
phase: Literal["completed"]
answer: str
sources: List[SourceMetadata]
class ErrorEvent(BaseModel):
phase: Literal["error"]
message: str
StreamingQueryEvent = Union[
DecomposedEvent, RetrievingEvent, FilteringEvent,
CompletedEvent, ErrorEvent
]

View File

@ -1,10 +1,11 @@
"""Query router for RAG pipeline."""
import json
import logging
from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse
from app.core.config import get_settings
from app.models.query import QueryRequest, QueryResponse
from app.models.query import QueryRequest
from app.models.common import SourceMetadata
from app.services.llm_client import LLMClient
from app.services.query_decomposer import QueryDecomposer
@ -17,11 +18,11 @@ router = APIRouter(tags=["query"])
NO_RESULTS_ANSWER = "I could not find any relevant information to answer your question."
@router.post("/query", response_model=QueryResponse)
async def query(request: QueryRequest):
if not request.question or not request.question.strip():
raise HTTPException(status_code=400, detail="Question is required")
def _format_sse(data: dict) -> str:
return f"data: {json.dumps(data)}\n\n"
async def _query_stream(request: QueryRequest):
settings = get_settings()
try:
@ -29,27 +30,50 @@ async def query(request: QueryRequest):
rag = RAGService(llm_client=llm_client, settings=settings)
logger.info("Query: %s", request.question)
decomposer = QueryDecomposer(llm_client)
extracted_questions = await decomposer.decompose(request.question)
logger.info("Extracted questions: %s", extracted_questions)
yield _format_sse({
"phase": "decomposed",
"extracted_questions": extracted_questions,
})
chunks = rag.retrieve(extracted_questions, n_results=settings.retrieval_n_results)
yield _format_sse({"phase": "retrieving"})
if not chunks:
return QueryResponse(extracted_questions=extracted_questions, answer=NO_RESULTS_ANSWER, sources=[])
yield _format_sse({
"phase": "completed",
"answer": NO_RESULTS_ANSWER,
"sources": [],
})
return
chunks_for_filter = [(text, meta) for text, meta, _dist in chunks]
relevance_filter = RelevanceFilter(llm_client)
yield _format_sse({"phase": "filtering"})
filtered = await relevance_filter.filter(
request.question, chunks_for_filter, threshold=settings.relevance_threshold
)
if not filtered:
return QueryResponse(extracted_questions=extracted_questions, answer=NO_RESULTS_ANSWER, sources=[])
yield _format_sse({
"phase": "completed",
"answer": NO_RESULTS_ANSWER,
"sources": [],
})
return
chunk_texts = [chunk for chunk, _meta in filtered]
chunk_metadata = [meta for _chunk, meta in filtered]
yield _format_sse({"phase": "generating"})
answer = await rag.generate_response(request.question, chunk_texts, chunk_metadata)
logger.info("Answer generated: %d chars, %d sources", len(answer), len(filtered))
@ -65,10 +89,32 @@ async def query(request: QueryRequest):
for meta in chunk_metadata
]
return QueryResponse(extracted_questions=extracted_questions, answer=answer, sources=sources)
yield _format_sse({
"phase": "completed",
"answer": answer,
"sources": [s.model_dump() for s in sources],
})
except HTTPException:
raise
except Exception as e:
logger.error("Query failed: %s", str(e))
raise HTTPException(status_code=500, detail=f"Query failed: {str(e)}")
logger.error("Query stream failed: %s", str(e))
yield _format_sse({
"phase": "error",
"message": f"Query failed: {str(e)}",
})
@router.post("/query")
async def query(request: QueryRequest):
if not request.question or not request.question.strip():
raise HTTPException(status_code=400, detail="Question is required")
return StreamingResponse(
_query_stream(request),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
},
)

View File

@ -1,7 +1,7 @@
import React from 'react'
export interface ExtractedQuestionsDisplayProps {
extractedQuestions?: string[]
extractedQuestions?: string[] | null
isLoading: boolean
}

View File

@ -9,6 +9,7 @@ interface ResponsePanelProps {
answer: string | null
sources: SourceMetadata[]
isLoading: boolean
phase?: string
error: string | null
}
@ -16,6 +17,7 @@ export const ResponsePanel: React.FC<ResponsePanelProps> = ({
answer,
sources,
isLoading,
phase,
error,
}) => {
const [sourcesExpanded, setSourcesExpanded] = useState<boolean>(true)
@ -42,9 +44,19 @@ export const ResponsePanel: React.FC<ResponsePanelProps> = ({
)
}
const phaseMessages: Record<string, string> = {
retrieving: 'Searching documents...',
filtering: 'Filtering relevant passages...',
generating: 'Generating answer...',
}
if (isLoading) {
return (
<div className="space-y-3 p-4">
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-4">
<div className="w-4 h-4 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin" />
<span>{phaseMessages[phase ?? ''] || 'Processing...'}</span>
</div>
<div
data-testid="skeleton-line"
className="h-4 bg-gray-200 rounded animate-pulse w-full"

View File

@ -1,5 +1,5 @@
import axios from 'axios'
import type { QueryRequest, QueryResponse, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse } from '../types'
import type { QueryRequest, QueryResponse, QueryStreamEvent, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse } from '../types'
const BASE_URL: string = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8000/api/v1'
@ -10,6 +10,44 @@ export const queryDocument = async (request: QueryRequest): Promise<QueryRespons
return resp.data
}
export const queryDocumentStream = async (
request: QueryRequest,
onEvent: (event: QueryStreamEvent) => void,
signal?: AbortSignal
): Promise<void> => {
const response = await fetch(`${BASE_URL}/query`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
signal,
})
if (!response.ok) {
const text = await response.text()
throw new Error(`HTTP ${response.status}: ${text}`)
}
const reader = response.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
onEvent(JSON.parse(data))
}
}
}
}
export const ingestDocument = async (file: File): Promise<IngestResponse> => {
const form = new FormData()
form.append('file', file)

View File

@ -1,7 +1,8 @@
import React from 'react'
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { queryDocument, ingestDocument, listDocuments, listChunks, deleteDocument, deleteChunk } from './api'
import type { QueryRequest, QueryResponse, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse } from '../types'
import { queryDocument, queryDocumentStream, ingestDocument, listDocuments, listChunks, deleteDocument, deleteChunk } from './api'
import type { QueryRequest, QueryResponse, QueryStreamEvent, SourceMetadata, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse } from '../types'
import { useState, useCallback, useRef } from 'react'
export const queryClient = new QueryClient()
@ -11,6 +12,98 @@ export const useQueryDocument = () => {
})
}
export interface QueryStreamState {
extractedQuestions: string[] | null
answer: string | null
sources: SourceMetadata[] | null
phase: 'idle' | 'decomposing' | 'retrieving' | 'filtering' | 'generating' | 'completed' | 'error'
error: Error | null
}
export const useQueryDocumentStream = () => {
const [state, setState] = useState<QueryStreamState>({
extractedQuestions: null,
answer: null,
sources: null,
phase: 'idle',
error: null,
})
const abortRef = useRef<AbortController | null>(null)
const mutate = useCallback(async (request: QueryRequest) => {
setState({
extractedQuestions: null,
answer: null,
sources: null,
phase: 'decomposing',
error: null,
})
abortRef.current = new AbortController()
try {
await queryDocumentStream(request, (event: QueryStreamEvent) => {
switch (event.phase) {
case 'decomposed':
setState(prev => ({
...prev,
extractedQuestions: event.extracted_questions ?? null,
phase: 'retrieving',
}))
break
case 'retrieving':
setState(prev => ({ ...prev, phase: 'retrieving' }))
break
case 'filtering':
setState(prev => ({ ...prev, phase: 'filtering' }))
break
case 'generating':
setState(prev => ({ ...prev, phase: 'generating' }))
break
case 'completed':
setState(prev => ({
...prev,
answer: event.answer ?? null,
sources: event.sources ?? null,
phase: 'completed',
}))
break
case 'error':
setState(prev => ({
...prev,
phase: 'error',
error: new Error(event.message ?? 'Unknown error'),
}))
break
}
}, abortRef.current.signal)
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') {
setState(prev => ({ ...prev, phase: 'idle' }))
return
}
setState(prev => ({
...prev,
phase: 'error',
error: err instanceof Error ? err : new Error(String(err)),
}))
}
}, [])
const reset = useCallback(() => {
abortRef.current?.abort()
setState({
extractedQuestions: null,
answer: null,
sources: null,
phase: 'idle',
error: null,
})
}, [])
return { ...state, mutate, reset }
}
export const useIngestDocument = () => {
return useMutation<IngestResponse, Error, File>({
mutationFn: ingestDocument,

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Film } from 'lucide-react'
import { Group, Panel, Separator } from 'react-resizable-panels'
import { useQueryDocument } from '../lib/queries'
import { useQueryDocumentStream } from '../lib/queries'
import { QueryInput } from '../components/QueryInput'
import { ExtractedQuestionsDisplay } from '../components/ExtractedQuestionsDisplay'
import { ResponsePanel } from '../components/ResponsePanel'
@ -18,12 +18,14 @@ const VideoPlaceholder: React.FC = () => {
}
export const LTTPage: React.FC = () => {
const queryMutation = useQueryDocument()
const queryStream = useQueryDocumentStream()
const handleQuerySubmit = (question: string): void => {
queryMutation.mutate({ question })
queryStream.mutate({ question })
}
const isLoading = queryStream.phase !== 'idle' && queryStream.phase !== 'completed' && queryStream.phase !== 'error'
return (
<div className="h-full bg-gray-50">
<Group
@ -38,8 +40,11 @@ export const LTTPage: React.FC = () => {
<VideoPlaceholder />
</div>
<div className="p-6 flex flex-col gap-4 overflow-y-auto min-h-0">
<QueryInput onSubmit={handleQuerySubmit} isLoading={queryMutation.isPending} />
<ExtractedQuestionsDisplay extractedQuestions={queryMutation.data?.extracted_questions} isLoading={queryMutation.isPending} />
<QueryInput onSubmit={handleQuerySubmit} isLoading={isLoading} />
<ExtractedQuestionsDisplay
extractedQuestions={queryStream.extractedQuestions}
isLoading={queryStream.phase === 'decomposing'}
/>
</div>
</div>
</Panel>
@ -51,10 +56,11 @@ export const LTTPage: React.FC = () => {
<Panel id="ltt-lower-panel" minSize="20%">
<div className="h-full p-6 border-t border-gray-200 overflow-y-auto">
<ResponsePanel
answer={queryMutation.data?.answer ?? null}
sources={queryMutation.data?.sources ?? []}
isLoading={queryMutation.isPending}
error={queryMutation.isError ? (queryMutation.error instanceof Error ? queryMutation.error.message : 'Query failed') : null}
answer={queryStream.answer}
sources={queryStream.sources ?? []}
isLoading={queryStream.phase === 'retrieving' || queryStream.phase === 'filtering' || queryStream.phase === 'generating'}
phase={queryStream.phase}
error={queryStream.error?.message ?? null}
/>
</div>
</Panel>

View File

@ -4,14 +4,18 @@ import { queryClient } from '../../lib/queries'
import App from '../../App'
import type { QueryResponse, IngestResponse } from '../../types'
vi.mock('../../lib/api', () => ({
queryDocument: vi.fn(),
ingestDocument: vi.fn(),
}))
vi.mock('../../lib/api', async () => {
const actual = await vi.importActual('../../lib/api')
return {
...(actual as Record<string, unknown>),
queryDocumentStream: vi.fn(),
ingestDocument: vi.fn(),
}
})
import { queryDocument, ingestDocument } from '../../lib/api'
import { queryDocumentStream, ingestDocument } from '../../lib/api'
const mockQueryDocument = vi.mocked(queryDocument)
const mockQueryDocumentStream = vi.mocked(queryDocumentStream)
const mockIngestDocument = vi.mocked(ingestDocument)
const mockQueryResponse: QueryResponse = {
@ -39,7 +43,17 @@ describe('Query flow integration (App-level)', () => {
beforeEach(() => {
queryClient.clear()
vi.restoreAllMocks()
mockQueryDocument.mockResolvedValue(mockQueryResponse)
mockQueryDocumentStream.mockImplementation(async (_request, onEvent) => {
onEvent({ phase: 'decomposed', extracted_questions: mockQueryResponse.extracted_questions })
onEvent({ phase: 'retrieving' })
onEvent({ phase: 'filtering' })
onEvent({ phase: 'generating' })
onEvent({
phase: 'completed',
answer: mockQueryResponse.answer,
sources: mockQueryResponse.sources,
})
})
mockIngestDocument.mockResolvedValue(mockIngestResponse)
})
@ -56,7 +70,17 @@ describe('Query flow integration (App-level)', () => {
})
it('full query flow: type question, submit, see questions and answer', async () => {
mockQueryDocument.mockResolvedValue(mockQueryResponse)
mockQueryDocumentStream.mockImplementation(async (_request, onEvent) => {
onEvent({ phase: 'decomposed', extracted_questions: mockQueryResponse.extracted_questions })
onEvent({ phase: 'retrieving' })
onEvent({ phase: 'filtering' })
onEvent({ phase: 'generating' })
onEvent({
phase: 'completed',
answer: mockQueryResponse.answer,
sources: mockQueryResponse.sources,
})
})
render(<App />)
@ -85,7 +109,9 @@ describe('Query flow integration (App-level)', () => {
})
it('handles API error gracefully', async () => {
mockQueryDocument.mockRejectedValue(new Error('Server error: 500'))
mockQueryDocumentStream.mockImplementation(async (_request, _onEvent) => {
throw new Error('Server error: 500')
})
render(<App />)

View File

@ -17,6 +17,14 @@ export interface QueryResponse {
sources: SourceMetadata[]
}
export interface QueryStreamEvent {
phase: 'decomposed' | 'retrieving' | 'filtering' | 'generating' | 'completed' | 'error'
extracted_questions?: string[]
answer?: string
sources?: SourceMetadata[]
message?: string
}
export interface IngestResponse {
document_id: string
chunk_count: number

Binary file not shown.