Q76: runs call the agent's own model, or fail plainly where the provider cannot
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8b4df5e11f
commit
8fcdd61bc5
5 files changed
+90
-9
No files matched your search
@@ -17,6 +17,7 @@ import { selectToolTurn, supportsToolTurn } from "./provider-tool-turns/router";
|
||||
import { resolveTurnCredential } from "./provider-tool-turns/credential-resolver";
|
||||
import { currentRunId } from "./run-control";
|
||||
import { budgetBlock, isPriced, promptChars, providerPrices } from "./spend";
|
||||
import { effectiveModel } from "./task-run-preparation";
|
||||
import {
|
||||
HEALTH_STATE_HEALTHY,
|
||||
WORKSPACE_SCOPES,
|
||||
@@ -116,15 +117,19 @@ export class ProviderResponseRunner {
|
||||
if (!supportsToolTurn(provider.provider_key)) {
|
||||
throw new Error("Agent execution is not available for this provider; assign the agent an OpenAI, Anthropic, xAI, Groq, Mistral, Cerebras, OpenRouter, GPT account, GitHub Copilot, Google Antigravity, or Claude Pro provider.");
|
||||
}
|
||||
const model = provider.model;
|
||||
// A call made for a run works for its agent: the agent's model, its budget, its attribution.
|
||||
const taskRunId = currentRunId();
|
||||
const agent = taskRunId
|
||||
? this.deps.db.prepare("select a.id, a.department, a.model_id from task_runs r join agents a on a.id = r.agent_id where r.id = ?").get(taskRunId) as { id: string; department: string; model_id: string | null }
|
||||
: null;
|
||||
// Azure calls the deployment stored with its credential, so an agent there cannot pick another model.
|
||||
if (provider.provider_key === "azure-openai" && agent?.model_id && agent.model_id !== provider.model) {
|
||||
throw new Error(`Azure OpenAI calls the deployment configured on the provider; this agent asks for "${agent.model_id}". Clear the agent's model or set it to "${provider.model}".`);
|
||||
}
|
||||
const model = effectiveModel(agent?.model_id ?? null, provider.model);
|
||||
if (!model) throw new Error("Provider model is required.");
|
||||
const owner: ProviderRow & { model: string } = { ...provider, model };
|
||||
const turn = this.deps.toolTurn ?? selectToolTurn(provider.provider_key);
|
||||
// A call made for a run is spent by its agent: refuse before calling once its budget is gone.
|
||||
const taskRunId = currentRunId();
|
||||
const agent = taskRunId
|
||||
? this.deps.db.prepare("select a.id, a.department from task_runs r join agents a on a.id = r.agent_id where r.id = ?").get(taskRunId) as { id: string; department: string }
|
||||
: null;
|
||||
const blocked = agent ? budgetBlock(this.deps.db, agent.id, isPriced(providerPrices(this.deps.db, provider.id)), new Date()) : null;
|
||||
if (blocked) throw new Error(blocked);
|
||||
// Everything the model reads: instructions, the conversation, and the tool definitions.
|
||||
|
||||
@@ -37,7 +37,7 @@ import { ConnectorGrantsService } from "./composio/connector-grants";
|
||||
import { ProviderCooldowns } from "./provider-cooldowns";
|
||||
import { HeartbeatScheduler } from "./heartbeat-scheduler";
|
||||
import {
|
||||
assertExecutionApproval, createIsolationContext, executionApprovalProblem, loadGoal, loadProject, loadWorkspace,
|
||||
assertExecutionApproval, createIsolationContext, effectiveModel, executionApprovalProblem, loadGoal, loadProject, loadWorkspace,
|
||||
requireAgentProvider, requireAssignedAgent, requireTask, sameWorkspaceAccess, workspaceAccess,
|
||||
} from "./task-run-preparation";
|
||||
|
||||
@@ -175,7 +175,7 @@ export class TaskExecutionService {
|
||||
let providerRequestId: string | null = null;
|
||||
let skillRuntimeSnapshot: SkillRuntimeSnapshotRow | null = null;
|
||||
try {
|
||||
skillRuntimeSnapshot = this.resolveSkillRuntimeSnapshot(orgId, agent.id, runId, skillBudgetTokens(provider.context_window_tokens), `${task.title}\n${task.description}`, { providerKey: provider.provider_key, model: provider.model }, task.writing_mode === WRITING_MODE.CONVERSATIONAL);
|
||||
skillRuntimeSnapshot = this.resolveSkillRuntimeSnapshot(orgId, agent.id, runId, skillBudgetTokens(provider.context_window_tokens), `${task.title}\n${task.description}`, { providerKey: provider.provider_key, model: effectiveModel(agent.model_id, provider.model) }, task.writing_mode === WRITING_MODE.CONVERSATIONAL);
|
||||
// Watermark for reply delivery and mid-run message polling.
|
||||
const runStartedAt = new Date().toISOString();
|
||||
const chatContext = loadChatWakeContext(this.db, orgId, agent.id, isolation.secretScopeJson);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { WRITING_MODE, type WritingMode } from "../../shared/domain";
|
||||
import { CEO_FIRST_HIRES, firstHiresFor, leadsPeople } from "./org-building";
|
||||
import { effectiveModel } from "./task-run-preparation";
|
||||
import { canonicalMentionHandle } from "../../shared/mention-handles";
|
||||
|
||||
const RECORD_LEARNINGS_INSTRUCTION =
|
||||
@@ -220,7 +221,7 @@ export function buildTaskPrompt(input: {
|
||||
`Requires filesystem: ${input.task.requires_filesystem === 1 ? "yes" : "no"}`,
|
||||
`Assignee: ${input.agent.name} (${input.agent.role})`,
|
||||
...(input.managerName ? [`Reports to: ${input.managerName}`] : []),
|
||||
`Provider: ${input.provider.label} ${input.provider.model ?? input.agent.model_id ?? ""}`.trim(),
|
||||
`Provider: ${input.provider.label} ${effectiveModel(input.agent.model_id, input.provider.model) ?? ""}`.trim(),
|
||||
`Execution context: ${input.isolation.executionContextId}`,
|
||||
`Service identity: ${input.isolation.serviceIdentityId}`,
|
||||
];
|
||||
|
||||
@@ -67,6 +67,11 @@ export function sameWorkspaceAccess(mountJson: string, current: Array<{ workspac
|
||||
return JSON.stringify(recorded) === JSON.stringify(current);
|
||||
}
|
||||
|
||||
/** The model a run calls: the agent's own choice, or else its provider's default. */
|
||||
export function effectiveModel(agentModelId: string | null, providerModel: string | null): string | null {
|
||||
return agentModelId?.trim() || providerModel;
|
||||
}
|
||||
|
||||
export function requireAgentProvider(db: DatabaseSync, orgId: string, agent: TaskExecutionAgentRow): TaskExecutionProviderRow {
|
||||
if (!agent.model_provider_id) throw new Error("Task assignee requires a model provider.");
|
||||
const provider = db.prepare(`
|
||||
|
||||
Reference in new issue
Block a user