# 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:8001` - `search`: `http://localhost:8002` - `qdrant`: `http://localhost:6334` - Inside compose, services use `QDRANT_URL=http://qdrant:6333` - Collection name from `.env`: `evaluation` - Vector names from `.env`: `dense` and `sparse` 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`. ```bash curl -sS http://localhost:8001/health curl -sS http://localhost:8002/health ``` Expected shape: ```json {"status":"ok"} ``` ### `POST /index` Schema from the PDF: - body root: `data` - `data.chat` - `data.overlap_messages[]` - `data.new_messages[]` Runnable request: ```bash 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: ```json { "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: ```bash curl -sS -X POST http://localhost:8001/sparse_embedding \ -H 'Content-Type: application/json' \ -d '{ "texts": [ "Релиз Go перенесли на следующую неделю", "VK GPT обсуждали в отдельном чате" ] }' ``` Observed response: ```json { "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`: ```bash curl -sS -X POST http://localhost:8002/search \ -H 'Content-Type: application/json' \ -d '{ "question": { "text": "Что писали про релиз Go?" } }' ``` Full schema from the PDF: ```json { "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: ```bash curl -sS http://localhost:8001/health curl -sS http://localhost:8002/health ``` Observed: ```json {"status":"ok"} {"status":"ok"} ``` ### 2. Qdrant collection exists, but starts empty Command: ```bash curl -sS http://localhost:6334/collections/evaluation ``` Observed before manual insert: - `points_count: 0` - `indexed_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[]` and `values[]` ### 5. `/search` on an empty collection returns an empty result Command: ```bash curl -sS -X POST http://localhost:8002/search \ -H 'Content-Type: application/json' \ -d '{"question":{"text":"Что писали про релиз Go?"}}' ``` Observed: ```json {"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_content` and `metadata.message_ids=["2"]` Command: ```bash 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: ```json {"result":{"operation_id":0,"status":"completed"},"status":"ok","time":0.008234969} ``` Collection state after insert: - `points_count: 1` - `indexed_vectors_count: 1` ### 7. `/search` works after one point is present Minimal request: ```bash curl -sS -X POST http://localhost:8002/search \ -H 'Content-Type: application/json' \ -d '{"question":{"text":"Что писали про релиз Go?"}}' ``` Observed: ```json {"results":[{"message_ids":["2"]}]} ``` Enriched request without `date_range`: ```bash 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: ```json {"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: ```json { "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 `search` code tries to pass them into a numeric `qdrant_client.models.Range` - so `date_range` is a real runtime bug in the current local build ## Bottom Line - `index /health`: OK - `search /health`: OK - `POST /index`: OK - `POST /sparse_embedding`: OK - `POST /search` on empty collection: OK, returns empty list - `POST /search` after one test point is inserted: OK - `POST /search` with enriched request excluding `date_range`: OK - `POST /search` with `date_range` from the PDF schema: FAILS in current implementation