Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,12 @@ All FastAPI route handlers use Pydantic models for request/response validation:
from pydantic import BaseModel
from fastapi import FastAPI


class UserRequest(BaseModel):
name: str
age: int


@app.post("/users")
async def create_user(user: UserRequest):
# Pydantic validates name is string, age is int
Expand Down
12 changes: 11 additions & 1 deletion DSL/CronManager/script/store_secrets_in_vault.sh
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,14 @@ setup_python_environment() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Failed to install loguru" >&2
return 1
}


# Install requests, required by LokiLogger which decrypt_vault_secrets.py imports
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Installing requests library..."
"$UV_BIN" pip install --python "$VENV_PATH/bin/python3" "requests>=2.32" 2>&1 || {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Failed to install requests" >&2
return 1
}

# Mark setup as complete
touch "$VENV_PATH/.setup_complete"

Expand Down Expand Up @@ -196,6 +203,9 @@ if ! setup_python_environment; then
exit 1
fi

# Set Python path
export PYTHONPATH="/app:/app/src:/app/src/vector_indexer:$PYTHONPATH"

# Function to determine platform name
get_platform_name() {
case "$llmPlatform" in
Expand Down
40 changes: 21 additions & 19 deletions docs/API_TOOL_CALLING.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,21 +209,21 @@ A `PointStruct` is built:

```python
PointStruct(
id = endpoint_id, # UUID used directly as Qdrant point ID
vector = {
"dense": [v1, v2, ..., v3072],
id=endpoint_id, # UUID used directly as Qdrant point ID
vector={
"dense": [v1, v2, ..., v3072],
"sparse": {"indices": [...], "values": [...]},
},
payload = { # Stored metadata — no extra DB lookup needed
"endpoint_id": "...",
"name": "get_national_holidays",
"description": "...",
"url": "https://openholidaysapi.org/PublicHolidays",
"method": "GET",
"params": [...],
payload={ # Stored metadata — no extra DB lookup needed
"endpoint_id": "...",
"name": "get_national_holidays",
"description": "...",
"url": "https://openholidaysapi.org/PublicHolidays",
"method": "GET",
"params": [...],
"enriched_context": "...",
"service_id": "...",
}
"service_id": "...",
},
)
```

Expand Down Expand Up @@ -306,9 +306,9 @@ Instantiated once in `ToolClassifier.__init__()` and reuses the shared Qdrant `h

```python
APISemanticSearcher(
embedding_service=orchestration_service, # generates dense embeddings
qdrant_client=self._qdrant_client, # shared connection pool
disambiguator=None, # optional: inject for testing
embedding_service=orchestration_service, # generates dense embeddings
qdrant_client=self._qdrant_client, # shared connection pool
disambiguator=None, # optional: inject for testing
)
```

Expand Down Expand Up @@ -861,7 +861,7 @@ Results are **deduplicated by endpoint name** (a single endpoint matched by two

```python
class ExecutionMode(str, Enum):
SINGLE = "single"
SINGLE = "single"
PARALLEL = "parallel"
```

Expand Down Expand Up @@ -947,7 +947,7 @@ await multi_loop.stream_run_turn(
chat_id=chat_id,
user_message=request.message,
conversation_history=conversation_history,
endpoint_states=session.parallel_endpoints, # list[EndpointSessionState]
endpoint_states=session.parallel_endpoints, # list[EndpointSessionState]
turn_count=session.turn_count,
awaiting_continuation=session.awaiting_continuation,
session_language=effective_session_language,
Expand Down Expand Up @@ -979,7 +979,9 @@ call_payloads = [
{**state.endpoint, "call_params": state.collected_params}
for state in parallel_endpoints
]
multi_result = await MultiAPICaller(api_caller).call_all(call_payloads, language=detected_language)
multi_result = await MultiAPICaller(api_caller).call_all(
call_payloads, language=detected_language
)
```

---
Expand Down Expand Up @@ -1402,7 +1404,7 @@ endpoint A sends a message that strongly matches endpoint B), both the session a
L2 key are cleaned up:

```python
await session_store.delete(request.chatId) # existing behaviour
await session_store.delete(request.chatId) # existing behaviour
if FeatureFlags.ATC_RESPONSE_CACHE_ENABLED:
await ATCCacheStore().invalidate_l2(request.chatId)
```
Expand Down
36 changes: 19 additions & 17 deletions docs/CONTEXT_WORKFLOW_GREETING_DETECTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,17 @@ In **Phase 2**, `ContextWorkflowExecutor` calls `get_greeting_response(greeting_

```python
GREETINGS_ET = {
"hello": "Tere! Kuidas ma saan sind aidata?",
"hello": "Tere! Kuidas ma saan sind aidata?",
"goodbye": "Nägemist! Head päeva!",
"thanks": "Palun! Kui on veel küsimusi, küsi julgelt.",
"casual": "Tere! Mida ma saan sinu jaoks teha?",
"thanks": "Palun! Kui on veel küsimusi, küsi julgelt.",
"casual": "Tere! Mida ma saan sinu jaoks teha?",
}

GREETINGS_EN = {
"hello": "Hello! How can I help you?",
"hello": "Hello! How can I help you?",
"goodbye": "Goodbye! Have a great day!",
"thanks": "You're welcome! Feel free to ask if you have more questions.",
"casual": "Hey! What can I do for you?",
"thanks": "You're welcome! Feel free to ask if you have more questions.",
"casual": "Hey! What can I do for you?",
}
```

Expand Down Expand Up @@ -358,29 +358,31 @@ Key log entries emitted during a request:

```python
class ConversationRound(BaseModel):
user_message: str # The user's message text
bot_message: str # The bot's response text
timestamp: float # Unix timestamp of the round
user_message: str # The user's message text
bot_message: str # The bot's response text
timestamp: float # Unix timestamp of the round
```

### `ConversationHistoryState` (Redis fetch result)

```python
class ConversationHistoryState(BaseModel):
chat_id: str # Unique conversation identifier
rounds: list[ConversationRound] # Ordered rounds (newest last), capped at 10
summary: Optional[str] # Incremental summary of evicted older rounds
chat_id: str # Unique conversation identifier
rounds: list[ConversationRound] # Ordered rounds (newest last), capped at 10
summary: Optional[str] # Incremental summary of evicted older rounds
```

### `ContextDetectionResult` (Phase 1 output)

```python
class ContextDetectionResult(BaseModel):
is_greeting: bool # True if query is a greeting
greeting_type: str # "hello" | "goodbye" | "thanks" | "casual"
can_answer_from_context: bool # True if query can be answered from history or summary
reasoning: str # LLM's brief explanation
answered_from_summary: bool # True when answer derived from summary path
is_greeting: bool # True if query is a greeting
greeting_type: str # "hello" | "goodbye" | "thanks" | "casual"
can_answer_from_context: (
bool # True if query can be answered from history or summary
)
reasoning: str # LLM's brief explanation
answered_from_summary: bool # True when answer derived from summary path
context_snippet: Optional[str] # Relevant excerpt for Phase 2 generation, or None
```

Expand Down
16 changes: 7 additions & 9 deletions docs/HYBRID_SEARCH_CLASSIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,7 @@ tokens = re.findall(r"\w+", text.lower()) # ["mis", "suhe", "on", "euro", ...]

```python
# Collection: "intent_collections"
vectors_config = {
"dense": VectorParams(size=3072, distance=Distance.COSINE)
}
vectors_config = {"dense": VectorParams(size=3072, distance=Distance.COSINE)}
sparse_vectors_config = {
"sparse": SparseVectorParams(index=SparseIndexParams(on_disk=False))
}
Expand Down Expand Up @@ -197,12 +195,12 @@ Queries Qdrant using only the dense vector to get **actual cosine similarity sco

```python
# classifier.py → _dense_search()
POST /collections/intent_collections/points/query
POST / collections / intent_collections / points / query
{
"query": [0.023, -0.041, ...], # 3072-dim dense vector
"using": "dense",
"limit": 6, # DENSE_SEARCH_TOP_K * 2 (3 * 2 = 6, allows dedup)
"with_payload": true
"limit": 6, # DENSE_SEARCH_TOP_K * 2 (3 * 2 = 6, allows dedup)
"with_payload": true,
}
```

Expand All @@ -219,15 +217,15 @@ Sparse prefetch is only included if the query produces a non-empty sparse vector
```python
# classifier.py → _hybrid_search()
# First checks collection exists and has data (points_count > 0)
POST /collections/intent_collections/points/query
POST / collections / intent_collections / points / query
{
"prefetch": [
{"query": dense_vector, "using": "dense", "limit": 10},
{"query": {"indices": [...], "values": [...]}, "using": "sparse", "limit": 10}
{"query": {"indices": [...], "values": [...]}, "using": "sparse", "limit": 10},
],
"query": {"fusion": "rrf"},
"limit": 5,
"with_payload": true
"with_payload": true,
}
```

Expand Down
20 changes: 11 additions & 9 deletions docs/REDIS_SESSION_STORE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ if session is None:
# No active session → this is a fresh conversation
...
else:
print(session.state) # "collecting_params"
print(session.collected_params) # {"city": "Tallinn"}
print(session.turn_count) # 2
print(session.state) # "collecting_params"
print(session.collected_params) # {"city": "Tallinn"}
print(session.turn_count) # 2
```

---
Expand Down Expand Up @@ -129,12 +129,14 @@ session_store = request.app.state.session_store
session = await session_store.get(request.chatId)
if session is None:
detected_endpoint = ... # endpoint detected from user query
await session_store.save(APIToolSession(
chat_id=request.chatId,
state="collecting_params",
selected_endpoint=detected_endpoint,
turn_count=1,
))
await session_store.save(
APIToolSession(
chat_id=request.chatId,
state="collecting_params",
selected_endpoint=detected_endpoint,
turn_count=1,
)
)
return "Which city would you like weather for?"

# --- Turn 2+ ---
Expand Down
9 changes: 6 additions & 3 deletions docs/TESTPRODUCTIONLLM_SERVICE_WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,15 @@ orchestration_service = self.orchestration_service
service_content = service_result["content"]
service_buttons = service_result["buttons"]


async def service_stream() -> AsyncIterator[str]:
yield orchestration_service.format_sse(
chat_id, service_content, service_buttons or None
)
yield orchestration_service.format_sse(chat_id, "END")
orchestration_service.log_costs(costs_metric)


return service_stream()
```

Expand All @@ -229,12 +231,13 @@ return service_stream()
**`llm_orchestration_service.py` → `format_sse()`** (line 1195):

```python
def format_sse(self, chat_id: str, content: str,
buttons: Optional[List[Dict[str, Any]]] = None) -> str:
def format_sse(
self, chat_id: str, content: str, buttons: Optional[List[Dict[str, Any]]] = None
) -> str:
inner_payload: Dict[str, Any] = {"content": content}
if buttons:
inner_payload["buttons"] = buttons

payload = {
"chatId": chat_id,
"payload": inner_payload,
Expand Down
Loading
Loading