b13a8afa01
Two connected features.
1) AI query interface (via the comm layer). communication_subroutine gains an
AI_QUERY_Q, a submit_ai_query() in-process entry point, and an authed
POST /ai_query endpoint ({prompt, channel_id, request_type?, username?}). The
prompt is queued and answered asynchronously by a new tasks.loop worker in the
always-loaded AI cog (Events), which calls handle_response - so it runs on
whichever backend $gadaj_teraz currently selects (GPT or Claude) - and posts the
answer to the requested channel, chunked to Discord's limit. request_type "NONE"
(default) is a clean one-shot: no persona system prompt, no memory write. The
worker starts before the OpenAI guard in cog_load, so it also runs on a
Claude-only box; cog_unload cancels it.
2) Librarian AI review. New command $wyszukaj_z_recenzja mirrors
$wyszukaj_linki_do_dokumentow but sets ai_review=True on the QueryControl, which
rides the round-trip and is matched back by UUID. When the hits return,
check_data_q sends the raw list as before, then - if flagged - hands the same
list (already in Crossref-relevance order) plus the search phrase to the AI
queue for a weighted re-rank and per-source review, delivered to the same
channel. QueryControl gains an ai_review flag (default False, so the orphan path
and all existing callers are unaffected).
Confirmed separately (and noted in the docs): the DOI list the AI receives is
pre-sorted by Crossref relevance - the librarian pipeline only filters (drops
title-less items) and splits (in-db / not-in-db), never re-sorts, and relies on
insertion-ordered dicts (Py 3.7+).
Verified: /ai_query auth (401/200/400/open), submit_ai_query and the queued
dict shape, and the QueryControl flag - via a Flask test client and
tests/integration/test_ai_query_endpoint.py (5 tests, all pass; integration
suite 11 passed, the 3 failures are the pre-existing /clear_pr_pls musician
tests fixed on a separate branch). Full first-party compile clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
1.9 KiB
Python
71 lines
1.9 KiB
Python
"""Integration: the bot's /ai_query endpoint enforces the shared key, validates
|
|
the payload, and queues accepted prompts onto AI_QUERY_Q for the AI worker.
|
|
"""
|
|
import communication_subroutine as cs
|
|
|
|
|
|
def _client(key="test-secret"):
|
|
cs.API_KEY = key
|
|
return cs.app.test_client()
|
|
|
|
|
|
def _drain():
|
|
while not cs.AI_QUERY_Q.empty():
|
|
cs.AI_QUERY_Q.get()
|
|
|
|
|
|
def test_ai_query_rejected_without_key():
|
|
_drain()
|
|
client = _client()
|
|
resp = client.post("/ai_query", json={"prompt": "x", "channel_id": 1})
|
|
assert resp.status_code == 401
|
|
assert cs.AI_QUERY_Q.empty() # nothing queued on a rejected call
|
|
|
|
|
|
def test_ai_query_accepted_with_key_and_queued():
|
|
_drain()
|
|
client = _client()
|
|
resp = client.post(
|
|
"/ai_query",
|
|
json={"prompt": "posortuj DOI", "channel_id": 42, "username": "siara"},
|
|
headers={"X-Conjurer-Api-Key": "test-secret"},
|
|
)
|
|
assert resp.status_code == 200
|
|
item = cs.AI_QUERY_Q.get()
|
|
assert item["prompt"] == "posortuj DOI"
|
|
assert item["channel_id"] == 42
|
|
assert item["username"] == "siara"
|
|
|
|
|
|
def test_ai_query_missing_prompt_is_rejected():
|
|
_drain()
|
|
client = _client()
|
|
resp = client.post(
|
|
"/ai_query",
|
|
json={"channel_id": 1},
|
|
headers={"X-Conjurer-Api-Key": "test-secret"},
|
|
)
|
|
assert resp.status_code == 400
|
|
assert cs.AI_QUERY_Q.empty()
|
|
|
|
|
|
def test_ai_query_open_when_key_unset():
|
|
_drain()
|
|
client = _client(key=None)
|
|
resp = client.post("/ai_query", json={"prompt": "y", "channel_id": 1})
|
|
assert resp.status_code == 200
|
|
|
|
|
|
def test_submit_ai_query_enqueues_expected_shape():
|
|
_drain()
|
|
cs.submit_ai_query(
|
|
prompt="P", channel_id=7, request_type="NONE", username="u", query_uuid="uid-1"
|
|
)
|
|
assert cs.AI_QUERY_Q.get() == {
|
|
"uuid": "uid-1",
|
|
"prompt": "P",
|
|
"channel_id": 7,
|
|
"request_type": "NONE",
|
|
"username": "u",
|
|
}
|