forked from zovos/vk_hackathon
- 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>
366 lines
10 KiB
Markdown
366 lines
10 KiB
Markdown
# 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
|