g1t/services/runner/src/model-env.ts
Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Agents as a team: lifecycle, merge queue, billing and a new shell | 1 | /** The kinds of work a g1t agent does. Each is routed on its own. */ |
| 2 | export type AgentTask = "implement" | "review" | "update" | "plan"; | |
| Members can read a private repository's pull request forks | 3 | |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 4 | /** Where one kind of work goes: what people see, and what is sent. */ |
| 5 | export type ModelRoute = { | |
| 6 | /** The model's public name, e.g. `Claude Sonnet 5.5`. */ | |
| 7 | modelName: string; | |
| 8 | /** The identifier sent to the provider. */ | |
| 9 | model: string; | |
| 10 | }; | |
| Members can read a private repository's pull request forks | 11 | |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 12 | /** |
| 13 | * g1t's routing policy. Nobody assigning an agent picks a model; the kind | |
| 14 | * of work decides, here, and the operator changes it in one place. | |
| 15 | */ | |
| 16 | export type AgentRoutes = Record<AgentTask, ModelRoute>; | |
| 17 | ||
| Members can read a private repository's pull request forks | 18 | /** The settings that decide where model requests go. */ |
| 19 | export type ModelRouting = { | |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 20 | /** |
| 21 | * The provider's key. Not needed when the gateway holds it and requests | |
| 22 | * authenticate to the gateway instead. | |
| 23 | */ | |
| Members can read a private repository's pull request forks | 24 | ANTHROPIC_API_KEY?: string; |
| 25 | /** A Cloudflare AI Gateway id; empty sends requests to the provider directly. */ | |
| 26 | AI_GATEWAY_ID: string; | |
| 27 | CLOUDFLARE_ACCOUNT_ID: string; | |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 28 | /** Authenticates to the gateway, if it requires it. */ |
| Members can read a private repository's pull request forks | 29 | AI_GATEWAY_TOKEN?: string; |
| 30 | }; | |
| 31 | ||
| Agents as a team: lifecycle, merge queue, billing and a new shell | 32 | /** What a run is for, attached to each of its requests at the gateway. */ |
| 33 | export type RunTags = { repo: string; pull: number }; | |
| 34 | ||
| 35 | /** Whether there is a way to reach a model at all. */ | |
| 36 | export function canReachModel(env: ModelRouting): boolean { | |
| 37 | return Boolean(env.ANTHROPIC_API_KEY || (env.AI_GATEWAY_ID && env.AI_GATEWAY_TOKEN)); | |
| 38 | } | |
| 39 | ||
| Members can read a private repository's pull request forks | 40 | /** Where the sandbox sends model requests, and what it sends with them. */ |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 41 | export function modelEnv( |
| 42 | env: ModelRouting, | |
| 43 | routes: AgentRoutes, | |
| 44 | task: AgentTask, | |
| 45 | tags: RunTags, | |
| 46 | ): Record<string, string> { | |
| 47 | const route = routes[task]; | |
| Members can read a private repository's pull request forks | 48 | const vars: Record<string, string> = { |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 49 | ANTHROPIC_MODEL: route.model, |
| Members can read a private repository's pull request forks | 50 | // Recorded at the top of the session, so anyone can see what ran. |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 51 | AGENT_MODEL_NAME: route.modelName, |
| Members can read a private repository's pull request forks | 52 | }; |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 53 | if (env.ANTHROPIC_API_KEY) vars.ANTHROPIC_API_KEY = env.ANTHROPIC_API_KEY; |
| 54 | if (!env.AI_GATEWAY_ID) return vars; | |
| 55 | ||
| 56 | vars.ANTHROPIC_BASE_URL = `https://gateway.ai.cloudflare.com/v1/${env.CLOUDFLARE_ACCOUNT_ID}/${env.AI_GATEWAY_ID}/anthropic`; | |
| 57 | // The gateway logs these with every request, so spend and failures can | |
| 58 | // be read per kind of work, repository and pull request. | |
| 59 | const headers = [`cf-aig-metadata: ${JSON.stringify({ task, ...tags })}`]; | |
| 60 | if (env.AI_GATEWAY_TOKEN) { | |
| 61 | vars.AI_GATEWAY_TOKEN = env.AI_GATEWAY_TOKEN; | |
| 62 | headers.push(`cf-aig-authorization: Bearer ${env.AI_GATEWAY_TOKEN}`); | |
| 63 | // With the provider's key stored in the gateway, the sandbox never | |
| 64 | // holds it. The harness still wants the variable set. | |
| 65 | vars.ANTHROPIC_API_KEY ??= env.AI_GATEWAY_TOKEN; | |
| Members can read a private repository's pull request forks | 66 | } |
| Agents as a team: lifecycle, merge queue, billing and a new shell | 67 | vars.ANTHROPIC_CUSTOM_HEADERS = headers.join("\n"); |
| Members can read a private repository's pull request forks | 68 | return vars; |
| 69 | } |