Commit Graph

42 Commits

Author SHA1 Message Date
gitea 25d6d4a3c0 AI: delete the dead OpenAI vector-store / file_search code
CI / compile (pull_request) Successful in 9s
CI / unit (pull_request) Successful in 24s
CI / integration (pull_request) Successful in 29s
file_search was confirmed unused, and these four functions were its only
implementation: create_vector_store, upload_files_to_vector_store,
add_files_to_vector_store and delete_files_from_vector_store, plus the
VECTOR_STORE_ID constant they were the only readers of.

Nothing called any of them - verified before removing - so they could not
crash anything, but they were the last references to beta.vector_stores and
beta.assistants: a sunset API that already took the whole AI cog down once
when a startup path touched it. Wiring them back up would have hit the same
404. Removing them means no path in this module can reach that API again.

54 lines gone, no behaviour change. Suite: 82 unit + 71 integration green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-28 09:39:56 +02:00
gitea c91ec03b83 AI: personal assistants without the dead API, and keep Ollama warm
CI / compile (pull_request) Successful in 5s
CI / unit (pull_request) Successful in 22s
CI / integration (pull_request) Successful in 26s
build / build (push) Successful in 12s
CI / compile (push) Successful in 5s
CI / unit (push) Successful in 22s
CI / integration (push) Successful in 26s
Two things the field report asked for.

1) PERSONAL ASSISTANTS (replacing the sunset OpenAI Assistants API)

The old implementation gave three capabilities. Two are reimplemented here,
the third was confirmed unused and is deliberately not replaced:

 * per-user persona - it already lived in system_gpt_settings.json; it was
   only ever being shipped to OpenAI. It is now the system prompt.
 * per-user conversation thread - OpenAI held this server-side. It now lives
   in assistant_memory.json, keyed by discord user id, trimmed to the most
   recent turns (CONJURER_ASSISTANT_MEMORY_TURNS) and written atomically so a
   torn write cannot lose someone's history. Deliberately a plain trim, not
   the AI summarisation used for the bar's shared memory: these are private
   DMs and must not end up in a public "legend".
 * file_search - not replaced. Confirmed not in use.

The conversation goes through handle_response with request_type="NONE" and an
explicit message list, which keeps it out of the bar's shared memory. The big
win: create_chat_assistant hardcoded model="gpt-4o", so assistants were locked
to OpenAI. They now run on whatever $gadaj_teraz selects - Claude and Ollama
included.

create_chat_assistant / chat_with_assistant are gone, and with them the last
call to beta.threads in the startup path - so the cog cannot be killed by that
API again. (add_files_to_vector_store / delete_files_from_vector_store still
reference beta.assistants but are dead code - nothing calls them - so they
cannot crash anything; left alone rather than widening this change.)

2) KEEPING A SELF-HOSTED MODEL WARM

Loading is the slow part - the GPU is shared with other users - so we preload
via Ollama's documented mechanism: /api/generate with a model, a keep_alive
and NO prompt. It loads the model and generates nothing.

 * on switching to ollama, $gadaj_teraz fires a preload in the BACKGROUND
   (not awaited: loading can take minutes and the command must answer at
   once), so the wait lands on the operator rather than the first user;
 * a warm loop re-asserts keep_alive every CONJURER_OLLAMA_WARM_MINUTES.

Both are hard-guarded on the ACTIVE provider being ollama. Warming a metered
API would burn tokens and money for nothing, so that guard is pinned by a test
asserting the preload is never called for gpt/claude, and another asserting the
preload body carries no prompt (a prompt would make every warm-up generate).

Tests: 82 unit + 71 integration green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 16:25:01 +02:00
gitea cfd19e2b34 AI: persist only the pinned field, not the whole config block
Adversarial review of the previous commit found a real regression it
introduced, reproduced against the actual code rather than inferred.

Changing _persist_active_ai_config from setdefault("configs", ...) to a
direct assignment made every backend switch write the whole in-memory
AI_CONFIGS over the settings file. Because AI_CONFIGS is now the built-in
defaults merged UNDER the file, that meant:

* an operator's hand edits were destroyed - and hand editing is the only
  way to change cheap_model / temperature / max_tokens, since
  set_active_model writes latest_model and there is no command for the rest,
* a config deliberately deleted from the file was re-seeded from the
  defaults and written back, permanently,
* pinning a model for one provider silently reverted another provider's
  entry,
* CONJURER_OLLAMA_MODEL stopped having any effect once the env-derived
  block had been persisted once.

The original motivation was still valid (plain setdefault would drop a
pinned model), so the fix is narrower rather than a revert: persist ONLY
the field this process actually changed. _persist_active_ai_config takes
model_for and writes back just that config's latest_model; everything else
in the on-disk block is left exactly as found. The constants.py merge stays
- it is what keeps a newly added provider visible after an upgrade - and is
now in-memory only, so it cannot reach the file.

Tests: the disk-write path had ZERO coverage, which is precisely how this
got in. Added four tests that drive the real _persist_active_ai_config
against a temp settings file: the pin lands while operator edits survive and
a deleted config is not resurrected; a plain switch leaves the configs block
byte-identical; a pin survives a re-read; a corrupt file does not raise.
Verified they have teeth - reintroducing the regression fails two of them.

Also hardened two weak tests the review caught: the pin test asserted on the
object set_active_model returns, which IS the mutated dict (so it passed
regardless), and the unconfigured-endpoint test monkeypatched OLLAMACLIENT
to None when it was already None, passing vacuously.

Suite: 72 unit + 70 integration green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 15:41:28 +00:00
gitea 6a6b821a0d AI: add a self-hosted Ollama backend, and let the picker choose the model
The bot could talk to OpenAI or Anthropic; this adds Ollama as a third
provider so it can run against models hosted on our own box, and extends
the switch command to pick WHICH model - not just which backend.

Provider: Ollama exposes an OpenAI-compatible /v1 surface, so the client is
just openai.AsyncOpenAI(base_url=OLLAMA_URL + "/v1"). That reuses the
existing message format and the whole _map_openai_error mapping instead of
forking a second error taxonomy. There is no API key - the endpoint IS the
configuration, so the backend stays dormant (and refuses to be selected,
with a message naming the variable) until CONJURER_OLLAMA_URL is set, the
same way the Conan bridge behaves.

Model selection:
* list_provider_models() asks the SERVER for Ollama (/v1/models), so the
  picker shows what is actually pulled on the box rather than a hardcoded
  list. Hosted providers just report what they are wired to.
* set_active_model() pins the config's latest_model and persists it;
  cheap_model is left alone so the MUSIC path keeps its cheaper backend.
* $gadaj_teraz now takes "<config> [model]", and a new read-only $modele_ai
  lists what is available. Pinning an id Ollama does not have is rejected up
  front with the real list - otherwise the typo only surfaces later as a
  failed reply.

Two fixes this exposed:
* AI_CONFIGS now merges built-in defaults with the settings-file block
  instead of letting the file win outright. Every provider switch persists a
  "configs" block, so a file written by an older build would have
  permanently hidden ollama from the picker after an upgrade.
* _persist_active_ai_config assigns "configs" instead of setdefault, so a
  pinned model actually survives a restart.
* the hardcoded 120s response timeout is now CONJURER_AI_TIMEOUT_SECONDS - a
  self-hosted model on a modest GPU can legitimately need longer.

Tests cover: ollama appears in the picker, select_model maps the legacy
gpt-4o default instead of leaking it, model listing (server-queried, sorted,
de-duplicated, failure -> AIError, unconfigured -> auth), pinning (latest
only, blank/unknown rejected), and that provider_generate routes to the new
path. Suite: 68 unit + 70 integration green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 15:41:28 +00:00
gitea defc482a22 fix: batch A - crash bugs, a leak, and startup/edge fragility
CI / compile (pull_request) Successful in 10s
CI / unit (pull_request) Successful in 19s
CI / integration (pull_request) Successful in 10s
build / build (push) Failing after 46m40s
CI / compile (push) Successful in 20s
CI / unit (push) Successful in 19s
CI / integration (push) Successful in 16s
Seven confirmed defects from the code audit, each small and low-risk.

* ai_functions.get_random_cyclic_message: random.randint(0, len(CYCLIC_WORDS))
  is inclusive -> could return len -> IndexError. Now randrange(len) + guard on
  an empty CYCLIC_WORDS.
* librarian_commands.get_image_sadox: random.randrange(0, len(res)-1) never
  picked the last comic and raised ValueError('empty range') on a single file.
  Now randrange(len) + an empty-dir guard.
* ai_commands image generation: every DALL-E error branch replied but did not
  return, so control fell through to `if response:` with response unbound ->
  UnboundLocalError right after the friendly message. Each branch now returns;
  response is pre-initialised; and PermissionDeniedError no longer passes a
  (message, text) tuple as a single arg.
* search_bot DOI match: `item["DOI"] in data` was a substring test, so a DOI
  that is a prefix of a longer one (10.1/1 vs 10.1/12) produced a false 'exists'
  hit. Now matches the line's first whitespace token exactly, via an O(1) dict
  index built once per consumer (also removes the O(queried-DOIs) per-line scan
  - a real win for large databases).
* communication_subroutine.scan_incoming: matched records were never removed
  from awaiting_q, so it grew unbounded over uptime and a reused UUID could
  re-match a stale record. Matched records are now dropped after dispatch.
* communication_subroutine.id3: (resp.headers.get("icy-name") or "").title()
  guards against a stream that omits headers (was AttributeError on None,
  500-ing the /prepped_tracks "next" handler).
* betoniarka.scan_tracks: waits for the radio logs to exist instead of dying
  with FileNotFoundError on a fresh deploy (which silently killed the
  now-playing forwarder until a restart).

Verified: tests/unit/test_search_bot.py gains exact-match and trailing-metadata
cases; full unit job 43 passed. Remaining observations (image-gen stale
/home/pi fallback paths + dead FileNotFoundError-after-OSError branch; tailer
still vulnerable to mid-run log rotation; DOI-first-token assumption) noted for
follow-up - none are crashes on the normal path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-31 23:01:27 +02:00
Michal Tuszowski 3d9d47aa90 ai: single-switch GPT/Claude backend for the chat cog
Wire the bot's AI chat pipeline (ai_functions.handle_response) to talk to
either OpenAI or the Anthropic Messages API, chosen by one active-config
switch. Behaviour on the default "gpt" config is unchanged.

constants.py:
* guarded `import anthropic` + CLAUDECLIENT (mirrors OPENAICLIENT), netrc
  machine 'anthropic' / ANTHROPIC_API_KEY;
* CLAUDE_LATEST_MODEL / CLAUDE_CHEAP_MODEL (opus-4-8 / haiku-4-5);
* AI_CONFIGS + DEFAULT_AI_CONFIG loaded from an optional 3rd element of
  system_gpt_settings.json (backward compatible - a 2-element file falls
  back to built-in defaults, active "gpt"). Single switch: CONJURER_AI_CONFIG
  env > settings "active" > "gpt".

ai_functions.py:
* provider_generate() dispatches to OpenAI (unchanged openai_call) or the new
  _anthropic_call() (splits system out, alternating messages, max_tokens,
  temperature omitted - Opus 4.8 rejects sampling params);
* AIError normalises both SDKs' exceptions into one category set so
  handle_response keeps its single set of in-character error replies;
* select_model() reads the active config; legacy "gpt-4o" default auto-maps
  to the active provider's model so the switch actually changes the backend;
* set_active_ai_config()/list_ai_configs() with best-effort persistence back
  into system_gpt_settings.json index 2.

ai_commands.py:
* $gadaj_teraz <config> hybrid command (Vykidailo-gated) switches backend at
  runtime;
* graceful guards when OPENAICLIENT is None: personal assistants (OpenAI
  Assistants API) and DALL-E image gen degrade instead of crashing, so a
  Claude-only deployment boots.

system_gpt_settings.json: add the configs block (gpt/claude/_template) as the
collection point for future backends. requirements_bot.txt: add anthropic.
bot.env.example: ANTHROPIC_API_KEY + CONJURER_AI_CONFIG. Unit tests cover the
message splitter, model selection, config listing, and error mapping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 21:34:59 +02:00
Michal Tuszowski a64fb2da57 Restructure: promote working_copy to repo root
Make the stable 'working copy' bot the canonical code at the repository
root so the install/deploy scripts operate against it again.

- Move working_copy/* to root (bot entrypoint is bot.py)
- Restore root-level install/ops scripts from c4fa88e (deploy.sh,
  install_main_bot.sh, status_report.*, conjurer.service, etc.)
- Fix deploy.sh: copy bot.py (was thin_client.py) and add the
  conanjurer_* modules; bump command count
- Remove side-by-side variant dirs (backup_old_docker, prototype_one,
  prototype_musician_one, musician_old, working_copy) and docker cruft
- Keep components as subdirs: conjurer_librarian, conjurer_musician,
  spotify_dl, yt_dlp, fonts, utils, docs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 23:50:55 +02:00
Michal Tuszowski 8e5e4ce530 Backup old. Preparation for forking 2026-06-16 14:09:27 +02:00
gitea 7ef1502249 upgrade to latest OpenAI API usage and logging improvements 2025-08-20 21:20:00 +02:00
gitea 2f13853084 Partial + reformatting 2025-08-20 18:25:23 +02:00
gitea f0bd6c4015 Latex incoming changes 2025-08-16 17:54:06 +02:00
gitea 6cc43ed16d fx and upgd 2025-08-16 17:36:57 +02:00
gitea 5b8d1ebb27 bgfx 2025-08-16 17:29:30 +02:00
gitea c0326288ff Added the following changes to the codebase: 2025-08-16 17:26:15 +02:00
gitea 97f43bc6a7 Update AI and librarian commands to use 'GENERAL' context" 2025-08-16 16:58:28 +02:00
gitea 21ec8d8bc9 Some would say it's ccr. But I'm not i*n. 2024-12-19 11:54:53 +01:00
gitea b513704a3d BGFX 2024-11-25 14:41:45 +01:00
gitea e4a47d025c safe 2024-11-15 19:59:50 +01:00
gitea 22730776d4 Fix ? 2024-11-15 01:08:34 +01:00
gitea ff2669d079 fix 2024-11-15 00:53:22 +01:00
gitea 4a6afa14cc bold commiting at the end of the day 2024-11-15 00:37:14 +01:00
gitea 5a74e813eb Work ongoing. 2024-11-14 23:13:18 +01:00
gitea be3f639573 aaaaaaa 2024-11-14 23:05:06 +01:00
gitea 620e6ddc06 Największy hit zespuło 100 tvarzy grzybiarzy - i nie jest o piosenka o zgubieniu czapeczki 2024-11-14 23:03:31 +01:00
gitea 6a6821ce4c Another one 2024-11-14 22:59:39 +01:00
gitea 2bb65bbc40 Another attempt 2024-11-14 22:58:14 +01:00
gitea aa63ffc754 aha 2024-11-14 22:56:34 +01:00
gitea 0dbb79719b Test 2024-11-14 22:56:05 +01:00
gitea 06d17a2e4d step forward 2024-11-14 22:34:17 +01:00
gitea ecc2e1f107 Deploy 2024-11-14 22:15:41 +01:00
gitea a981209399 Maybe ready ? 2024-11-14 22:11:37 +01:00
gitea 885f492f69 nxtfx 2024-11-14 22:00:37 +01:00
gitea 5363969456 fx 2024-11-14 21:59:09 +01:00
gitea 637926973c tst 2024-11-14 21:48:24 +01:00
gitea b15c2f06b8 Work ongoing - rich presence and ai assistant 2024-11-14 19:53:40 +01:00
gitea 64669887a3 fx 2024-11-13 22:53:19 +01:00
gitea 6d3ce5d714 bgfx 2024-11-13 22:51:27 +01:00
gitea 651d796254 Logfix 2024-11-13 22:10:43 +01:00
gitea b3e5a1aaf3 Start 2024-11-13 21:57:14 +01:00
gitea 9a0f869839 Main refactoring 2024-11-10 15:03:44 +01:00
gitea c4e0dea7da Refactoring step one 2024-11-07 09:54:52 +01:00
gitea c33cfc27ba New stuff incoming 2024-10-03 13:47:02 +02:00