- Add logviewer/: Dozzle web UI (port 9999) + analyze.py CLI tool - docker-compose.yml: add json-file logging with rotation and labels for index/search - Fix Dockerfiles: COPY *.py . so all modules are included in image - Convert all relative imports to flat absolute imports for Docker flat layout - Rename index/schemas.py → index/index_schemas.py to avoid module name collision with search/schemas.py in test runner - Update all tests to add service dir to sys.path and use flat imports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
10 KiB
Curl API Test
Sources
- Canonical contracts:
doc/ТЗ_на_хакатон_Индексация_и_поиск_по_сообщениям.pdf - Runnable examples and local launch notes:
README.md - Actual local wiring:
docker-compose.yml
PDF gives the strict request/response schemas for POST /index, POST /sparse_embedding, and POST /search.
README.md adds ready curl examples for the minimal requests.
This file normalizes both into checks against the current local compose stack.
Compose Wiring
index:http://localhost:8001search:http://localhost:8002qdrant:http://localhost:6334- Inside compose, services use
QDRANT_URL=http://qdrant:6333 - Collection name from
.env:evaluation - Vector names from
.env:denseandsparse
Note: current docker-compose.yml publishes Qdrant as 6334:6333, while README.md still says localhost:6333. For local checks in this repo state, use localhost:6334.
Extracted API Requests
GET /health
Both services must answer 200 OK.
curl -sS http://localhost:8001/health
curl -sS http://localhost:8002/health
Expected shape:
{"status":"ok"}
POST /index
Schema from the PDF:
- body root:
data data.chatdata.overlap_messages[]data.new_messages[]
Runnable request:
curl -sS -X POST http://localhost:8001/index \
-H 'Content-Type: application/json' \
-d '{
"data": {
"chat": {
"id": "chat-1",
"name": "Go Nova",
"sn": "chat-1@chat.agent",
"type": "channel",
"is_public": true
},
"overlap_messages": [
{
"id": "1",
"time": 1710000000,
"text": "Обсуждаем релиз Go",
"sender_id": "u1",
"file_snippets": "",
"parts": [],
"mentions": [],
"member_event": null,
"is_system": false,
"is_hidden": false,
"is_forward": false,
"is_quote": false
}
],
"new_messages": [
{
"id": "2",
"time": 1710000060,
"text": "Релиз Go перенесли на следующую неделю",
"sender_id": "u2",
"file_snippets": "",
"parts": [],
"mentions": [],
"member_event": null,
"is_system": false,
"is_hidden": false,
"is_forward": false,
"is_quote": false
}
]
}
}'
Observed response:
{
"results": [
{
"page_content": "u1: Обсуждаем релиз Go\nu2: Релиз Go перенесли на следующую неделю",
"dense_content": "[2024-03-09 16:00] sender:u1\nОбсуждаем релиз Go\n[2024-03-09 16:01] sender:u2\nРелиз Go перенесли на следующую неделю",
"sparse_content": "u1 Обсуждаем релиз Go u2 Релиз Go перенесли на следующую неделю",
"message_ids": ["2"]
}
]
}
Note: overlap messages are used as context, but are not included in returned message_ids.
POST /sparse_embedding
Schema from the PDF:
- body root:
texts: string[]
Runnable request:
curl -sS -X POST http://localhost:8001/sparse_embedding \
-H 'Content-Type: application/json' \
-d '{
"texts": [
"Релиз Go перенесли на следующую неделю",
"VK GPT обсуждали в отдельном чате"
]
}'
Observed response:
{
"vectors": [
{
"indices": [275068001, 108710752, 842257583, 1159207840, 2129888840, 703082301],
"values": [1.6652868125369606, 1.6652868125369606, 1.6652868125369606, 1.6652868125369606, 1.6652868125369606, 1.6652868125369606]
},
{
"indices": [73209461, 751565418, 59863655, 1856729543, 2036701913, 1943620510],
"values": [1.6652868125369606, 1.6652868125369606, 1.6652868125369606, 1.6652868125369606, 1.6652868125369606, 1.6652868125369606]
}
]
}
POST /search
Minimal request from README.md:
curl -sS -X POST http://localhost:8002/search \
-H 'Content-Type: application/json' \
-d '{
"question": {
"text": "Что писали про релиз Go?"
}
}'
Full schema from the PDF:
{
"question": {
"text": "Что писали про релиз Go?",
"asker": "u2",
"asked_on": "2024-03-09",
"variants": ["релиз go перенесли?", "обсуждение релиза go"],
"hyde": ["В чате пишут, что релиз Go перенесли на следующую неделю."],
"keywords": ["релиз", "Go", "перенесли"],
"entities": {
"people": ["u2"],
"emails": [],
"documents": [],
"names": ["Go"],
"links": []
},
"date_mentions": ["следующая неделя", "2024-03-09"],
"date_range": {
"from": "2024-03-09T00:00:00Z",
"to": "2024-03-10T00:00:00Z"
},
"search_text": "релиз Go перенесли на следующую неделю"
}
}
Checks Run
1. Health checks
Commands:
curl -sS http://localhost:8001/health
curl -sS http://localhost:8002/health
Observed:
{"status":"ok"}
{"status":"ok"}
2. Qdrant collection exists, but starts empty
Command:
curl -sS http://localhost:6334/collections/evaluation
Observed before manual insert:
points_count: 0indexed_vectors_count: 0
This matches the README note that local compose creates the collection, but the template flow does not automatically upsert /index output into Qdrant.
3. /index works
Observed:
- HTTP request completed successfully
- service returned one chunk
- returned fields match the contract:
page_content,dense_content,sparse_content,message_ids
4. /sparse_embedding works
Observed:
- HTTP request completed successfully
- response returned
vectors[] - each vector contains
indices[]andvalues[]
5. /search on an empty collection returns an empty result
Command:
curl -sS -X POST http://localhost:8002/search \
-H 'Content-Type: application/json' \
-d '{"question":{"text":"Что писали про релиз Go?"}}'
Observed:
{"results":[]}
This is expected while evaluation has no points.
6. Manual Qdrant upsert for end-to-end smoke test
To verify /search end-to-end, I inserted one synthetic point into local Qdrant with:
- point id
1001 - dummy dense vector of size
1024 - sparse vector under field
sparse - payload containing
page_contentandmetadata.message_ids=["2"]
Command:
vec=$(awk 'BEGIN{for(i=0;i<1024;i++) printf "%s%d", (i?",":""), (i==0)}')
curl -sS -X PUT 'http://localhost:6334/collections/evaluation/points?wait=true' \
-H 'Content-Type: application/json' \
-d "{\"points\":[{\"id\":1001,\"vector\":{\"dense\":[${vec}],\"sparse\":{\"indices\":[1],\"values\":[1.0]}},\"payload\":{\"page_content\":\"u1: Обсуждаем релиз Go\\nu2: Релиз Go перенесли на следующую неделю\",\"metadata\":{\"message_ids\":[\"2\"],\"participants\":[\"u1\",\"u2\"],\"start\":\"2024-03-09T16:00:00Z\",\"end\":\"2024-03-09T16:01:00Z\",\"chat_id\":\"chat-1\",\"chat_name\":\"Go Nova\",\"chat_type\":\"channel\",\"chat_sn\":\"chat-1@chat.agent\"}}}]}"
Observed:
{"result":{"operation_id":0,"status":"completed"},"status":"ok","time":0.008234969}
Collection state after insert:
points_count: 1indexed_vectors_count: 1
7. /search works after one point is present
Minimal request:
curl -sS -X POST http://localhost:8002/search \
-H 'Content-Type: application/json' \
-d '{"question":{"text":"Что писали про релиз Go?"}}'
Observed:
{"results":[{"message_ids":["2"]}]}
Enriched request without date_range:
curl -sS -X POST http://localhost:8002/search \
-H 'Content-Type: application/json' \
-d '{
"question": {
"text": "Что писали про релиз Go?",
"asker": "u2",
"asked_on": "2024-03-09",
"variants": ["релиз go перенесли?", "обсуждение релиза go"],
"hyde": ["В чате пишут, что релиз Go перенесли на следующую неделю."],
"keywords": ["релиз", "Go", "перенесли"],
"entities": {
"people": ["u2"],
"emails": [],
"documents": [],
"names": ["Go"],
"links": []
},
"date_mentions": ["следующая неделя", "2024-03-09"],
"search_text": "релиз Go перенесли на следующую неделю"
}
}'
Observed:
{"results":[{"message_ids":["2"]}]}
8. Defect: date_range request currently fails
The full PDF-shaped request with ISO timestamps in question.date_range does not work in the current implementation.
Observed:
{
"detail": "2 validation errors for Range\ngte\n Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='2024-03-09T00:00:00Z', input_type=str]\n For further information visit https://errors.pydantic.dev/2.12/v/float_parsing\nlte\n Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='2024-03-10T00:00:00Z', input_type=str]\n For further information visit https://errors.pydantic.dev/2.12/v/float_parsing"
}
Interpretation:
- the public request schema accepts ISO date strings
- current
searchcode tries to pass them into a numericqdrant_client.models.Range - so
date_rangeis a real runtime bug in the current local build
Bottom Line
index /health: OKsearch /health: OKPOST /index: OKPOST /sparse_embedding: OKPOST /searchon empty collection: OK, returns empty listPOST /searchafter one test point is inserted: OKPOST /searchwith enriched request excludingdate_range: OKPOST /searchwithdate_rangefrom the PDF schema: FAILS in current implementation