feat(ai): run the AI analyst through any Gemini-compatible gateway - #334
Open
tsstod wants to merge 1 commit into
Open
feat(ai): run the AI analyst through any Gemini-compatible gateway#334tsstod wants to merge 1 commit into
tsstod wants to merge 1 commit into
Conversation
|
@tsstod is attempting to deploy a commit to the Developing Osiris' projects Team on Vercel. A member of the Team first needs to authorize it. |
The analyst could only talk to Google, and only with a GEMINI_API_KEY_*
set. Without one, /api/ai/analyze and /api/ai/briefing returned 503 and
the one-click overview fell back to the heuristic.
OSIRIS_AI_BASE_URL now points the existing Gemini SDK at any server that
speaks the Gemini REST API, and OSIRIS_AI_MODEL overrides the model id.
A local OmniRoute, for example, serves
/v1beta/models/{model}:generateContent and routes each request to a
provider it picks ('auto'). No Gemini key is needed then. The SDK still
insists on one, so a stand-in is passed (OSIRIS_AI_API_KEY if the
gateway wants a real one). With neither variable set, behaviour is
unchanged.
Reasoning models behind a gateway return their chain of thought as
parts flagged `thought: true`. SDK 0.24's text() concatenates every
part, so briefings opened with the model talking to itself. The
responseText() helper drops those parts. It still calls text() first,
so a blocked response keeps surfacing as SAFETY_BLOCKED.
The three routes each carried their own copy of the key loop; they now
share getServerApiKeys(). The overview label read "GEMINI 2.0 FLASH"
whatever answered, so the route now returns the model id and the label
shows that.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tsstod
force-pushed
the
feat/ai-gateway
branch
from
September 11, 2026 15:56
4a46c0f to
16e9284
Compare
This was referenced Sep 11, 2026
Author
|
The red Vercel – osiris check here is Vercel waiting for a team member to authorize a deploy from a fork. No build has run, so here's a local one instead:
🤖 Generated with Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lets the AI analyst (
/api/ai/analyze,/api/ai/briefing,/api/ai/overview) run against any server that speaks the Gemini REST API, not just Google. With neither new variable set, nothing changes.Why
The analyst could only talk to
generativelanguage.googleapis.com, and only with aGEMINI_API_KEY_*set. Without one, analyze and briefing return503 NO_API_KEYand the one-click overview drops to the heuristic analyst..env.exampledidn't mention the Gemini keys at all, so a fresh self-hosted install never gets a model.Gateways such as OmniRoute serve Gemini's own endpoint (
/v1beta/models/{model}:generateContent) and route each request to whatever provider they're configured with, free-tier ones included. The Gemini SDK already accepts abaseUrl, so a gateway needs no second client.What changed
OSIRIS_AI_BASE_URLpoints the existing SDK at the gateway.OSIRIS_AI_MODELoverrides the model id (default staysgemini-2.0-flash;autolets OmniRoute choose). Both are read per request, not at import.OSIRIS_AI_API_KEYis used if the gateway wants a real one. RealGEMINI_API_KEY_*values still win when both are set.thought: true. SDK 0.24'sresponse.text()concatenates every part, so a briefing opened with the model talking to itself. The newresponseText()drops those parts. It still callstext()first, so a blocked response keeps surfacing asSAFETY_BLOCKED.GEMINI_API_KEY_1..8loop; they now sharegetServerApiKeys()..env.examplegains an AI ANALYST section covering the Gemini keys and the gateway variables, including the Docker note: usehost.docker.internal, not127.0.0.1.Testing
src/lib/ai-engine.test.ts(9 tests) stubsfetchand asserts on what the real SDK sends. That covers the default Google URL, the gateway URL and model, thought-part stripping, plain replies unchanged, and key selection.tsc --noEmitis clean.eslinton the touched files reports onlyno-explicit-anyerrors that already exist onmaster, at the same count per file.OSIRIS_AI_BASE_URL=http://127.0.0.1:20128,OSIRIS_AI_MODEL=auto): overview and briefing both answered through the gateway, and the briefing came back without reasoning text. AI OVERVIEW in the Live Alerts panel rendered a model read-out labelledAUTO.master(8781ae3, fix(map): restore the basemap's proxy path, and stop the banner covering a working map #335): the fullnpx vitest runpasses on a local branch that combines this PR with feat(recon): let GITHUB_TOKEN lift the GitHub recon rate limit #337 and fix(ui): keep every tool-strip flyout inside the window #338 — 49 test files passed, 614 tests passed. The 2 skipped files and 16 skipped tests are pre-existing skips of the network-gated tests.Notes
AiOverview.tsx,ai-engine.tsand.env.example, so it doesn't conflict with feat(recon): let GITHUB_TOKEN lift the GitHub recon rate limit #337 or fix(ui): keep every tool-strip flyout inside the window #338; both of those are also one commit on currentmaster, and the three merge cleanly together.🤖 Generated with Claude Code