vk_hackathon/doc/curl_api_test.md
q 1f976cf297 Add logviewer project and fix Docker imports
- 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>
2026-04-18 15:26:11 +03:00

10 KiB
Raw Blame History

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.

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.chat
  • data.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: 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:

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_content and metadata.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: 1
  • indexed_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 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