Skip to content

Gecko Suite for GAM: enrichment, embeddings and the new assistant - #58

Merged
davior merged 5 commits into
mainfrom
claude/beautiful-wright-og4xur
Nov 2, 2026
Merged

davior merged 5 commits into
mainfrom
claude/beautiful-wright-og4xur

Conversation

@davior

@davior davior commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

M2 for GAM: SA-1, SA-2, SA-5, SA-6 and SA-7 in gecko-suite's plan. Two parts, on one branch.

Merging this changes nothing until AI_BACKEND=suite is set. local is the default and the old path is untouched, so the move is one setting in either direction. The one addition that is not behind the switch is two new, empty tables (see Deployment).

1. Enrichment and embeddings on the suite (SA-1, 5, 6, 7)

With AI_BACKEND=suite, build_provider and build_embedder ask Gecko Suite. These are the two functions every enrichment job and the search already call, so no job changed shape. app/suite.py is everything GAM knows about the suite.

  • Background jobs have no user session, so the suite is called as GAM on behalf of the asset's owner, with SUITE_APP_SECRET, issued on the suite's Apps screen. The app refuses to start in suite mode without it. The credential can use the gateway but never change anyone's settings.
  • Each kind of enrichment names its own job: enrich.describe, enrich.summarize, enrich.autotag (attribute follows summarize), so an administrator can route them to different models. The spend is attributed to the asset (gam:asset:<id>).
  • The suite counts the spend, so record_completion writes nothing in this mode and nothing is counted twice. Speech and image-generation usage stays GAM's own until the suite takes those (M3).
  • A suite that is down doesn't stop the library: build_embedder answers None so search falls back to keywords; queueing enrichment is a 503, not a crash; a page import falls back to bare links; jobs fail with one sentence and retry.
  • Embedding width stays 512 (SUITE_EMBEDDING_DIMENSIONS), what the existing index is wide.

Settings says where AI is managed and links to the suite's Providers, Routing and Usage, instead of a provider form that would never be used. Its Usage panel stays.

2. The assistant (SA-2)

A side panel (the sparkles button in the header) for asking about the library and having it tidied. Only with AI_BACKEND=suite; otherwise the panel says so and offers no text box.

  • It proposes; you apply. A turn ends with at most one proposal: renames, descriptions, summaries, tag adds and removes, and enrichment passes to run. Each asset's part shows what would change, with a tick to leave some out. Nothing in the library changes until Apply. The tools that touch data only look (search_assets, get_asset, read_transcript, list_tags) or record a proposal (propose_changes, propose_enrichment). There is no tool that writes.
  • Apply goes through the services the REST API uses: description and summary are stamped as the AI's, an approved name as a person's (a name is the one field the library never lets an AI replace on its own, and here a person pressed Apply), tag changes reindex search, and enrichment passes are queued as jobs. Applying twice is refused.
  • Saved conversations; one background job per turn. A turn finishes with nobody watching and is waiting, with its proposal, when you come back. Called as GAM for the person who asked, so a restart just reruns an interrupted turn.
  • Job chat.assistant, so it can have a model of its own. A model without the tools capability is refused with a sentence saying where to change it.
  • The model chooses the ids, so every tool re-checks ownership. Someone else's asset is reported as missing, the way the REST API does, and a test pins that.
  • Built on run_agent in gecko-suite-client, the loop extracted from GVC.

Not yet: generating images from the panel (suite M3), and reading pictures the person attaches. Both are additions to the tool list, not changes to the shape.

Review notes:

  • Writing this found a bug in my SuiteEmbedder: GAM reads embedder.model before any embed() call. Fixed in davior/gecko-suite#3; this PR pins the commit that has it.
  • The client is pinned to a gecko-suite commit by archive URL because python:3.13-slim has no git. I'll re-pin to main once that PR merges.
  • docker-compose.prod.yml gains suite.geckopico.com in extra_hosts: containers can't reach the server's own public address, so without it enrichment would fail in production while every test passed.

Test plan

  • cd backend && pytest -q: 1518 passed. That includes 15 tests for suite-mode enrichment and 17 for the assistant: status, a turn end to end, apply once, apply a subset, discard, enrichment queued on apply, ownership, a no-op proposal, suite down mid-turn, stop before start, replayed history, one turn at a time, another user's conversation, delete.
  • cd frontend && npm run format:check && npm run lint && npm test && npm run build: 470 tests passed; build typechecks.
  • Real-process smoke test of enrichment against a stand-in suite (earlier commit).
  • CI green, including the Docker image build, which installs the client from GitHub. I couldn't build the image here (no Docker daemon).
  • The panel in a real browser against a real suite. Tested here only against a faked API.

Deployment

Merging and redeploying changes nothing for users until you switch AI_BACKEND.

  1. Before pulling: nothing. To use the suite, it must be running, with:
    • a model routed for Assistant (the other chat jobs fall back to it). The assistant needs one with the tools capability; ideally one that can also see images, for describe;
    • an embedding model routed for Search embeddings. Choose the same model your library was embedded with (by default text-embedding-3-small), or search loses its semantic half until you run Backfill under Settings.
  2. Issue GAM's secret. On the suite's Apps screen, issue one for GAM. It's shown once.
  3. Configuration (.env; all optional until you switch):
    • AI_BACKEND=local is the default. Set it to suite to switch.
    • SUITE_APP_SECRET: the secret from step 2. Required once AI_BACKEND=suite; the app won't start without it.
    • SUITE_BASE_URL=https://suite.geckopico.com and SUITE_EMBEDDING_DIMENSIONS=512 are the defaults. Leave the second alone under a populated library.
    • ASSISTANT_CONCURRENCY (2), ASSISTANT_MAX_ROUNDS (16), ASSISTANT_HISTORY_MESSAGES (20), ASSISTANT_TRANSCRIPT_CHARS (12000): defaults are fine.
  4. Deploy. Rebuilds the backend and the frontend, and the build downloads the client from GitHub, so the server needs outbound access to github.com:
    git pull origin main
    docker compose up --build -d
    docker compose ps
    curl -f http://localhost:${APP_PORT}/api/health
  5. Migration. c3a95e7d1f42 (add_assistant_tables) runs on container start and creates two new tables, assistant_session and assistant_turn. It touches no existing data and downgrades by dropping them. Nothing needs backing up first.
  6. Switch. Set SUITE_APP_SECRET and AI_BACKEND=suite in .env, then docker compose up -d. A restart, not a rebuild.
  7. Other apps: nothing.
  8. Verify:
    • Settings shows "AI is managed in Gecko Suite" with working links.
    • Describe an asset and get a description; a row appears in the suite's Usage under GAM.
    • Search a phrase; semantic search still works and coverage in Settings is unchanged.
    • Open the Assistant (sparkles button). Ask "which assets have no description?". It searches, answers, and offers a proposal. Discard it and confirm nothing changed, then ask again and Apply.
    • With AI_BACKEND=local, the panel says it needs the suite.
  9. Roll back: set AI_BACKEND=local and run docker compose up -d. Your providers and embedding settings are still there. A job running at the moment of the switch fails and can be retried. Saved conversations stay in the database, unused.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V1geD2ELWgL2sDpxYbx3Aw

With AI_BACKEND=suite, build_provider and build_embedder (the two
functions every enrichment job and the search already call) ask Gecko
Suite instead of the providers and embedding settings this app keeps.
They return objects with the same small interfaces as before, so no job
changed shape: app/suite.py is everything GAM knows about the suite.
"local" stays the default and the old path is untouched; the switch is a
restart, in either direction.

Enrichment is background work with no user session, so the suite is
called as GAM on behalf of the asset's owner (SUITE_APP_SECRET, issued on
the suite's Apps screen). The app refuses to start in suite mode without
it, rather than every job failing hours later with a message that says
nothing about why. Such a credential can use the gateway but never change
anyone's settings.

Describe, summarize and autotag each name their own job, so an
administrator can route them to different models (a seeing one for
describe, a cheap one for tagging); attribute follows summarize. The
suite attributes the spend to the asset (gam:asset:<id>) and counts it,
so record_completion writes nothing in this mode: nothing is counted
twice. Speech and image-generation usage stays GAM's own until the suite
takes those.

A suite that is down does not stop the library. build_embedder answers
None, exactly as for an unconfigured embedder, so search falls back to
keywords; queueing enrichment is a 503 rather than a crash; a page import
falls back to the bare links; jobs fail with one sentence and retry.

The embedding width is SUITE_EMBEDDING_DIMENSIONS, 512: what this app has
always asked OpenAI's text-embedding-3 models for, and what the existing
index is wide, so switching under a populated library keeps it valid when
the suite routes the same model.

Settings says where AI is managed and links to the suite's Providers,
Routing and Usage instead of a provider form and embedding settings that
would never be used. The production overlay gains the suite's public name
in extra_hosts (containers cannot reach the server's own public address).
The client is pinned to a gecko-suite commit by archive URL, since
python:3.13-slim has no git.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1geD2ELWgL2sDpxYbx3Aw
…osals to apply

A conversation is saved; each turn runs as a job on the suite (chat.assistant) as GAM
for the person who asked, with tools to search, read, list tags and propose. The turn
ends with at most one proposal, stored on the turn row. Nothing in the library changes
until Apply, which goes through the existing services (AI provenance for description
and summary, human for an approved name, reindex on tag changes, enrichment jobs
queued as usual). Only with AI_BACKEND=suite; local mode says so.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1geD2ELWgL2sDpxYbx3Aw
…osals to apply

A Sparkles button in the header opens a panel beside the page. Its state is in a store
(each route remounts the shell), a running turn is followed by polling and picked back up
when reopened, and a proposal shows what would change per asset with a tick to leave
some out. Says so plainly when the app isn't on Gecko Suite or no model is routed.
Records the design decisions in the plan and CLAUDE.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1geD2ELWgL2sDpxYbx3Aw
@davior davior changed the title Enrichment and embeddings on Gecko Suite behind AI_BACKEND Gecko Suite for GAM: enrichment, embeddings and the new assistant Nov 2, 2026
The assistant uses run_agent's max_tokens_message, which the earlier pin predates.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1geD2ELWgL2sDpxYbx3Aw
main's merge of gecko-suite#3 predates the fix (davior/gecko-suite#4), so CI failed on
SuiteEmbedder.resolve. Re-pin to main once #4 merges.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1geD2ELWgL2sDpxYbx3Aw
@davior
davior marked this pull request as ready for review November 2, 2026 11:39
@davior
davior merged commit 4186385 into main Nov 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants