Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -463,11 +463,13 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review
# # synthesis = both review, then ONE merged decision.
# AI_ON_MERGE=either # synthesis merge rule: either (block if EITHER reviewer flags) |
# # both (block only when both do). Ignored unless AI_COMBINE=synthesis.
# AI_DAILY_NEURON_BUDGET=10000000 # daily spend cap (Cloudflare Workers AI "neurons") shared by AI
# # summaries, the free consensus-defect reviewer pair, and the
# # AI-slop scorer. Default 10,000,000/day; clamped to [0, 10000000].
# # Only meters FREE Workers-AI calls — BYOK/provider calls above bill
# # the maintainer's own account and are not counted against this.
# AI_DAILY_NEURON_BUDGET=10000000 # daily spend cap (unit name is a Cloudflare Workers AI "neurons"
# # holdover; applied as a provider-agnostic heuristic budget) shared
# # by AI summaries, the free/default consensus-defect reviewer pair,
# # and the AI-slop scorer. Default 10,000,000/day; clamped to
# # [0, 10000000]. Only meters the free/default-reviewer path —
# # BYOK/provider calls above bill the maintainer's own account and
# # are not counted against this.
# Ollama reviewer (AI_PROVIDER=ollama). Defaults: OLLAMA_AI_BASE_URL=http://localhost:11434/v1,
# OLLAMA_AI_MODEL=llama3.1, no API key. Set the base URL to http://ollama:11434/v1 when using the compose
# --profile ollama service.
Expand Down
2 changes: 1 addition & 1 deletion .gittensory.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ gate:
minScore: 60
# aiReview: # opt-in AI maintainer review (off by default; needs the AI flags enabled)
# mode: advisory # block | advisory | off — block only blocks on a dual-model consensus defect
# byok: false # use a maintainer Anthropic/OpenAI key for the write-up; consensus stays free Workers AI
# byok: false # use a maintainer Anthropic/OpenAI key for the write-up; consensus stays on the free/default reviewer
# allAuthors: false # true reviews every PR author with the selected self-host model(s)
# provider: anthropic # anthropic | openai — which BYOK provider (the secret key is set via the dashboard, never here)
# model: claude-3-5-sonnet-latest # optional model override for the BYOK write-up
Expand Down
2 changes: 1 addition & 1 deletion src/config/gittensory-repo-focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ gate:
minScore: 60
# aiReview: # opt-in AI maintainer review (off by default; needs the AI flags enabled)
# mode: advisory # block | advisory | off — block only blocks on a dual-model consensus defect
# byok: false # use a maintainer Anthropic/OpenAI key for the write-up; consensus stays free Workers AI
# byok: false # use a maintainer Anthropic/OpenAI key for the write-up; consensus stays on the free/default reviewer
# allAuthors: false # true reviews every PR author with the selected self-host model(s)
# provider: anthropic # anthropic | openai — which BYOK provider (the secret key is set via the dashboard, never here)
# model: claude-3-5-sonnet-latest # optional model override for the BYOK write-up
Expand Down
5 changes: 3 additions & 2 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -962,8 +962,9 @@ async function recordAiKeyChange(

/**
* Decrypt a repo's BYOK key for an AI call. Returns null when no key is configured OR the encryption
* secret is unavailable OR decryption fails — so the caller silently falls back to free Workers AI and
* a misconfiguration never blocks the review. The plaintext key must be used immediately and never cached.
* secret is unavailable OR decryption fails — so the caller silently falls back to the free/default
* reviewer and a misconfiguration never blocks the review. The plaintext key must be used immediately
* and never cached.
*/
export async function getDecryptedRepositoryAiKey(env: Env, fullName: string): Promise<DecryptedRepositoryAiKey | null> {
const secret = env.TOKEN_ENCRYPTION_SECRET;
Expand Down
6 changes: 6 additions & 0 deletions src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ declare global {
PUBLIC_SITE_ORIGIN?: string;
AI_SUMMARIES_ENABLED?: string;
AI_PUBLIC_COMMENTS_ENABLED?: string;
/** Model id for a genuine Cloudflare Workers AI binding only — no live deployment (hosted or self-host)
* binds `env.AI` to Workers AI today (see CONVERGENCE_RUNBOOK.md), and self-host discards any
* `@cf/`-prefixed value here. Self-host operators should use the provider-specific `*_AI_MODEL` vars below. */
WORKERS_AI_SUMMARY_MODEL?: string;
/** Daily spend cap in Cloudflare Workers AI "neurons" for the free/default-reviewer path (shared across
* ai-review/ai-slop/ai-summaries/planner). The unit name is a Workers-AI holdover; it's applied as a
* provider-agnostic heuristic budget regardless of which configured provider actually serves the request. */
AI_DAILY_NEURON_BUDGET?: string;
/** Per-repository/day cap for maintainer-paid BYOK AI review provider calls. */
AI_BYOK_DAILY_REPO_LIMIT?: string;
Expand Down
5 changes: 3 additions & 2 deletions src/review/adapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ export function reviewVectorAdapter(vectorize: Vectorize): VectorAdapter {
};
}

// ── Inference (Workers AI → InferenceAdapter). Feature-gated. Mirrors `ai.run(model, options)`; the cast
// bridges the overloaded Workers-AI `run` to the portable single-signature shape. ──
// ── Inference (the Ai-shaped adapter → InferenceAdapter). Feature-gated. Mirrors `ai.run(model, options)`;
// the cast bridges the overloaded `run` signature to the portable single-signature shape. `ai` is
// Workers AI historically, and on self-host is the generic provider router (src/selfhost/ai.ts). ──
export function reviewInferenceAdapter(ai: Ai): InferenceAdapter {
return { run: (model, options) => (ai as unknown as { run(m: string, o: Record<string, unknown>): Promise<unknown> }).run(model, options) };
}
Expand Down
13 changes: 9 additions & 4 deletions src/review/rag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
// additive module. The host injects concrete adapters at the call site.

// ── Injected infra interfaces (inlined from reviewbot src/platform/types.ts) ──────────────────────
// These mirror the platform-adapter shapes so the host can pass its Vectorize/Workers-AI/D1-backed
// These mirror the platform-adapter shapes so the host can pass its Vectorize/self-host-AI/D1-backed
// implementations unchanged; nothing here depends on env bindings.

/** Vector search surface (Vectorize → Qdrant / pgvector / sqlite-vec). */
Expand All @@ -43,7 +43,8 @@ export interface VectorAdapter {
deleteByIds(ids: string[]): Promise<void>;
}

/** Inference (Workers AI → Ollama / OpenAI-compatible). Mirrors `ai.run(model, options)`. */
/** Inference (the configured AI provider — self-host Codex/Claude Code/Ollama/OpenAI-compatible, or the
* legacy Workers-AI binding). Mirrors `ai.run(model, options)`. */
export interface InferenceAdapter {
run(model: string, options: Record<string, unknown>): Promise<unknown>;
}
Expand Down Expand Up @@ -87,7 +88,10 @@ export type RagRetrievalResult = {
};

/** bge-m3: large context window → a whole file/function embeds as one coherent chunk (fewer vectors
* than 512-token models, which helps both quality and the free-tier vector budget). */
* than 512-token models, which helps both quality and the free-tier vector budget). This is a Workers-AI
* model id; the self-host embed path (`createOpenAiCompatibleAi` in src/selfhost/ai.ts) discards any
* `@cf/`-prefixed id and substitutes its own configured/default embed model (`AI_EMBED_MODEL`), so this
* constant only matters for a genuine Cloudflare Workers AI inference binding. */
export const EMBED_MODEL = "@cf/baai/bge-m3";
/** Default bge-m3 output dimension. Self-host can override this when QDRANT_DIM selects another model width. */
export const RAG_DIMENSIONS = 1024;
Expand All @@ -96,7 +100,8 @@ const CHUNK_CHARS = 16000; // per-file chunk budget; only files larger than this
const CHUNK_OVERLAP = 1500;
/** Hard per-repo stored-vector cap — the free-tier guard. Source is prioritized so it survives the cap. */
export const MAX_CHUNKS_PER_REPO = 1500;
const EMBED_BATCH = 96; // Workers AI caps embedding input at 100 items/call
const EMBED_BATCH = 96; // Workers AI caps embedding input at 100 items/call; kept as a conservative general
// bound — other embed providers (Ollama/vLLM/etc via the self-host adapter) may not share this exact cap.
const MAX_CONTEXT_CHARS = 14000; // bound the injected block (mirrors diff/knowledge budgets)
export const MAX_FILE_BYTES = 1_000_000; // skip files larger than ~1MB

Expand Down
12 changes: 7 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,8 @@ export type RepositorySettings = {
/** Slop-risk threshold (0-100) at/above which `slopGateMode: block` blocks. Default 60 (the `high` band). */
slopGateMinScore?: number | null | undefined;
/** AI-assisted slop advisory (the `slopAiAdvisory` capability). When true AND `slopGateMode != off`, a
* free Workers-AI pass adds an ADVISORY-only `ai_slop_advisory` finding for semantic slop the
* free/default-reviewer pass (the configured self-host provider, or the legacy Workers-AI pair when
* none is configured) adds an ADVISORY-only `ai_slop_advisory` finding for semantic slop the
* deterministic detector cannot quantify. It NEVER feeds slopRisk or the gate (only the deterministic
* core blocks). Default false — opt-in via `.gittensory.yml gate.slop.aiAdvisory`. */
slopAiAdvisory: boolean;
Expand All @@ -686,13 +687,14 @@ export type RepositorySettings = {
* like every other blocker). Default `off` — AI is opt-in. */
aiReviewMode: GateRuleMode;
/** Bring-your-own-key: when true and a provider key is configured for the repo, the advisory AI review
* is generated by the maintainer's frontier model (Anthropic/OpenAI) instead of free Workers AI. The
* consensus blocker always uses the free Workers-AI model pair regardless, so BYOK never changes who
* is generated by the maintainer's frontier model (Anthropic/OpenAI) instead of the free/default
* reviewer. The consensus blocker always uses the free/default reviewer pair regardless (the configured
* self-host provider, or the legacy Workers-AI pair when none is configured), so BYOK never changes who
* can be blocked. Default false. */
aiReviewByok: boolean;
/** Config-as-code BYOK provider for the advisory write-up. `null` = use the configured key's own
* provider. When set, it must match the stored key's provider or BYOK is skipped (Workers-AI fallback).
* The secret key itself is never here — only via the encrypted key store. */
* provider. When set, it must match the stored key's provider or BYOK is skipped (falls back to the
* free/default reviewer). The secret key itself is never here — only via the encrypted key store. */
aiReviewProvider?: "anthropic" | "openai" | null | undefined;
/** Config-as-code model override for the BYOK advisory write-up (e.g. "claude-3-5-sonnet-latest").
* `null` = use the key record's model, else a conservative per-provider default. */
Expand Down
Loading