Skip to content

Core logicReal OpenRouter Agent Pipeline + Per-Agent Token Optimizer, Telemetry, and Validation Hardening - #4

Merged
7vignesh merged 6 commits into
mainfrom
core-logic
Mar 26, 2026
Merged

Core logicReal OpenRouter Agent Pipeline + Per-Agent Token Optimizer, Telemetry, and Validation Hardening#4
7vignesh merged 6 commits into
mainfrom
core-logic

Conversation

@7vignesh

Copy link
Copy Markdown
Owner

Summary

This PR upgrades the backend from mock-agent execution to real OpenRouter-powered agents and introduces a full token optimization and observability workflow.

Key Additions

  • Real provider cutover (OpenRouter, real-only runtime path)
  • Per-agent token optimization controls (budgets, compression, dynamic output caps)
  • Strict schema-based quality guardrails with fail-fast behavior
  • Expanded telemetry for per-agent token and cost analysis
  • New tests for optimizer behavior, provider parsing/errors, and orchestrator failure propagation

What Changed

1) Real Provider Integration

  • Replaced mock runtime provider with OpenRouter initialization
  • Added environment-driven OpenRouter config validation
  • Preserved provider abstraction for future extensibility

2) Token Optimizer (Core Logic)

  • Extended agent config with:
    • Model routing
    • Max input/output tokens
    • Minimum output token floor
    • Compression level
    • Overflow retry attempts
  • Implemented adaptive compression passes
  • Added deterministic fail-fast behavior when budgets cannot be met
  • Introduced dynamic output token capping based on remaining per-agent budget

3) Prompt Strategy

  • Added compact, per-agent prompt builders
  • Reduced prompt size by sending only required context per agent
  • Enforced strict JSON-only output instructions to reduce parsing drift

4) Quality Guardrails

  • Added strict Zod validation at agent execution boundaries
  • Validation failures:
    • Propagate as explicit agent failures
    • Fail the job deterministically

5) Telemetry & Tuning Loop

  • Expanded agent_completed telemetry with:
    • inputTokens
    • outputTokens
    • totalTokens
    • Optimizer and provider diagnostics
    • Model and cache metadata
  • Added per-job token usage summaries
  • Enabled run-to-run comparison via Jobs API responses
  • Updated README with runtime config and token tuning guidance

6) Test Coverage (Phase 6)

  • Added optimizer unit tests:
    • Compression behavior
    • Budget failure scenarios
  • Added OpenRouter provider tests:
    • JSON parsing and usage extraction
    • Non-JSON error handling
  • Added orchestrator failure propagation test:
    • Schema validation failure path
  • Enabled dedicated @stackforge/agents test script

Validation Performed

  • ✅ Shared build
  • ✅ Agents typecheck
  • ✅ Agents tests
  • ✅ API typecheck
  • ✅ API integration tests

All checks passing.


Breaking / Behavioral Changes

  • Runtime now uses real OpenRouter provider (mock path removed)
  • Missing OpenRouter environment configuration causes early failure
  • Telemetry payload expanded (non-breaking, additive changes)

Follow-ups

  • Add SSE replay assertion for agent_completed.payload.totalTokens in API integration tests
  • Add lightweight reporting endpoint or dashboard using aggregated token usage

Copilot AI review requested due to automatic review settings March 26, 2026 18:56
@7vignesh
7vignesh merged commit fb65576 into main Mar 26, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR cuts over the backend orchestration layer from mock provider execution to real OpenRouter-backed agent calls, adding per-agent token budgeting/optimization, strict output validation, and expanded SSE + Jobs API telemetry for token/cost analysis.

Changes:

  • Added OpenRouter provider implementation and wired API runtime to use it (env-configured).
  • Introduced per-agent token optimizer (compression, budget guardrails, dynamic output caps) and strict Zod schema validation at agent boundaries.
  • Expanded SSE agent_completed telemetry and added Jobs API token-usage summaries + related integration tests.

Reviewed changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/shared/src/types/agent.types.ts Extends AgentConfig contract to include model + token optimizer controls.
packages/shared/src/schemas/sse.schema.ts Expands agent_completed payload schema with token/optimizer/provider metadata.
packages/agents/test/token-optimizer.test.ts Adds unit tests for optimizer compression + budget fail-fast behavior.
packages/agents/test/orchestrator-failure.test.ts Verifies schema validation failures propagate as agent failure + orchestrator rejection.
packages/agents/test/openrouter-provider.test.ts Adds tests for OpenRouter JSON parsing + non-JSON error handling.
packages/agents/src/provider/provider.interface.ts Extends provider interface to accept call options + expose usage/model metadata.
packages/agents/src/provider/openrouter.provider.ts Implements real OpenRouter chat completion calls + JSON parsing + usage extraction.
packages/agents/src/provider/mock.provider.ts Updates mock provider to match new provider interface + token fields.
packages/agents/src/orchestrator/orchestrator.service.ts Emits enriched agent_completed SSE events and threads new metrics through orchestrator.
packages/agents/src/optimizer/token.optimizer.ts Adds token estimation, compression passes, budget enforcement, and output token capping.
packages/agents/src/index.ts Exports new provider + optimizer from @stackforge/agents.
packages/agents/src/config/agent.configs.ts Defines per-agent model + budget + compression settings.
packages/agents/src/agents/prompts/index.ts Adds compact per-agent prompt building with strict JSON-only instructions.
packages/agents/src/agents/output.schemas.ts Defines per-agent Zod output schemas via BlueprintSchema.pick.
packages/agents/src/agents/base.agent.ts Integrates optimizer + provider options + strict schema validation + telemetry fields.
packages/agents/package.json Adds a Bun test script and @types/bun.
bun.lock Locks @types/bun addition.
apps/api/test/integration.test.ts Updates integration test expectations for failed status + token usage in job responses.
apps/api/src/store/job.store.ts Adds token usage summarization + job listing helper.
apps/api/src/services/generate.service.ts Switches runtime to OpenRouter provider with env validation + lazy singleton creation.
apps/api/src/routes/index.ts Adds /jobs listing route.
apps/api/src/controllers/jobs.controller.ts Adds list jobs controller and includes token usage in job responses.
apps/api/.env.example Documents required OpenRouter env variables.
README.md Documents OpenRouter runtime config, token tuning workflow, and SSE/job telemetry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +138 to +152
let selectedPass = 1;

for (let pass = 0; pass < attempts; pass++) {
const plan = buildCompressionPlan(config.compressionLevel, pass);
const compressedInput = compressInput(input, config.maxInputTokens, plan);
const compactInput = clampJsonSize(JSON.stringify(compressedInput), config.maxInputTokens);
const tokenEstimate = estimateTokens(compactInput);

selectedInput = compressedInput;
selectedPrompt = compactInput;
estimatedInputTokens = tokenEstimate;
selectedPass = pass + 1;

if (tokenEstimate <= config.maxInputTokens) {
break;

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compressionPasses is always at least 1 because the loop always runs and selectedPass is set to pass + 1 even when the input already fits without any trimming. That makes the telemetry misleading (a “pass” is reported even when no compression was needed). Consider tracking whether any trimming actually occurred and reporting 0 passes when the original prompt is within budget, or renaming the metric to reflect “attempts” rather than “compression passes”.

Suggested change
let selectedPass = 1;
for (let pass = 0; pass < attempts; pass++) {
const plan = buildCompressionPlan(config.compressionLevel, pass);
const compressedInput = compressInput(input, config.maxInputTokens, plan);
const compactInput = clampJsonSize(JSON.stringify(compressedInput), config.maxInputTokens);
const tokenEstimate = estimateTokens(compactInput);
selectedInput = compressedInput;
selectedPrompt = compactInput;
estimatedInputTokens = tokenEstimate;
selectedPass = pass + 1;
if (tokenEstimate <= config.maxInputTokens) {
break;
let selectedPass = 0;
if (estimatedInputTokens > config.maxInputTokens) {
for (let pass = 0; pass < attempts; pass++) {
const plan = buildCompressionPlan(config.compressionLevel, pass);
const compressedInput = compressInput(input, config.maxInputTokens, plan);
const compactInput = clampJsonSize(JSON.stringify(compressedInput), config.maxInputTokens);
const tokenEstimate = estimateTokens(compactInput);
selectedInput = compressedInput;
selectedPrompt = compactInput;
estimatedInputTokens = tokenEstimate;
selectedPass = pass + 1;
if (tokenEstimate <= config.maxInputTokens) {
break;
}

Copilot uses AI. Check for mistakes.
Comment on lines 75 to +83
tokensUsed: response.tokensUsed,
inputTokens: response.inputTokens ?? optimized.estimatedInputTokens,
outputTokens: response.outputTokens ?? optimized.maxOutputTokens,
totalTokens: response.tokensUsed,
estimatedInputTokens: optimized.estimatedInputTokens,
compressionPasses: optimized.compressionPasses,
providerInputTokens: response.inputTokens ?? optimized.estimatedInputTokens,
providerOutputTokens: response.outputTokens ?? optimized.maxOutputTokens,
model: response.model ?? optimized.model,

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback assignments make inputTokens and providerInputTokens identical, and outputTokens and providerOutputTokens identical, so the emitted telemetry doesn’t actually distinguish “estimated” vs “provider-reported” values. Also, defaulting outputTokens to optimized.maxOutputTokens when the provider doesn’t report usage will systematically overstate output usage/cost. Consider: (1) keep inputTokens/outputTokens as optimizer estimates, (2) only set provider*Tokens when the provider reports them (otherwise 0/null), and (3) compute totalTokens consistently from the chosen sources.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +47
function agentCompleted(
jobId: string,
agent: AgentName,
durationMs: number,
cached: boolean,
inputTokens: number,
outputTokens: number,
totalTokens: number,
tokensUsed: number,
estimatedInputTokens: number,
compressionPasses: number,
providerInputTokens: number,
providerOutputTokens: number,
model: string,
): SSEEvent {

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agentCompleted(...) takes 12 positional parameters, which is easy to misorder and hard to extend safely as telemetry evolves. Consider passing a single typed payload object (or the full AgentRunResult) and constructing the SSE payload from named properties to reduce the risk of accidental field swaps.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +35
const original = AGENT_CONFIGS.devops.minOutputTokens;
AGENT_CONFIGS.devops.minOutputTokens = AGENT_CONFIGS.devops.tokenBudget;

try {
expect(() =>
optimizeAgentPayload("devops", {
prompt: "x".repeat(40000),
stack: { backend: "express", database: "postgres" },
entities: Array.from({ length: 200 }, (_, index) => ({ name: `Entity${index}` })),
}),
).toThrow("Insufficient output token budget");
} finally {
AGENT_CONFIGS.devops.minOutputTokens = original;
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates the shared AGENT_CONFIGS singleton (devops.minOutputTokens) which can introduce cross-test flakiness if tests/files run in parallel. Prefer building a local config override (e.g., allow optimizeAgentPayload to accept an optional config object), or deep-clone/restore the entire agent config in a beforeEach/afterEach to avoid leaking state across tests.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +80
const response = await fetch(this.endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
"HTTP-Referer": this.appUrl,
"X-Title": this.appName,
},
body: JSON.stringify({
model: options.model,
messages: [
{ role: "system", content: options.systemPrompt },
{ role: "user", content: options.userPrompt },
],
max_tokens: options.maxOutputTokens,
temperature: options.temperature,
response_format: { type: "json_object" },
}),
});

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch is called without any timeout/abort handling. With real provider cutover, a hung connection can leave orchestration jobs stuck in running indefinitely (and tie up server resources). Consider adding an AbortController with a reasonable per-call timeout and surfacing a clear timeout error, so failures are deterministic and operationally safe.

Suggested change
const response = await fetch(this.endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
"HTTP-Referer": this.appUrl,
"X-Title": this.appName,
},
body: JSON.stringify({
model: options.model,
messages: [
{ role: "system", content: options.systemPrompt },
{ role: "user", content: options.userPrompt },
],
max_tokens: options.maxOutputTokens,
temperature: options.temperature,
response_format: { type: "json_object" },
}),
});
const controller = new AbortController();
const timeoutMs = 60000;
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
let response: Response;
try {
response = await fetch(this.endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
"HTTP-Referer": this.appUrl,
"X-Title": this.appName,
},
body: JSON.stringify({
model: options.model,
messages: [
{ role: "system", content: options.systemPrompt },
{ role: "user", content: options.userPrompt },
],
max_tokens: options.maxOutputTokens,
temperature: options.temperature,
response_format: { type: "json_object" },
}),
signal: controller.signal,
});
} catch (error) {
if (error instanceof Error && (error as any).name === "AbortError") {
throw new Error(`OpenRouter request timed out after ${timeoutMs}ms`);
}
throw error;
} finally {
clearTimeout(timeoutId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +177
let selectedPrompt = JSON.stringify(input);
let estimatedInputTokens = estimateTokens(selectedPrompt);
let selectedPass = 1;

for (let pass = 0; pass < attempts; pass++) {
const plan = buildCompressionPlan(config.compressionLevel, pass);
const compressedInput = compressInput(input, config.maxInputTokens, plan);
const compactInput = clampJsonSize(JSON.stringify(compressedInput), config.maxInputTokens);
const tokenEstimate = estimateTokens(compactInput);

selectedInput = compressedInput;
selectedPrompt = compactInput;
estimatedInputTokens = tokenEstimate;
selectedPass = pass + 1;

if (tokenEstimate <= config.maxInputTokens) {
break;
}
}

if (estimatedInputTokens > config.maxInputTokens) {
throw new Error(
`Token budget exceeded for agent '${agentName}' after ${attempts} compression attempts`,
);
}

const remainingBudget = config.tokenBudget - estimatedInputTokens;
if (remainingBudget < config.minOutputTokens) {
throw new Error(
`Insufficient output token budget for agent '${agentName}' after compression`,
);
}

const cappedOutputTokens = Math.min(config.maxOutputTokens, remainingBudget);

const prompt = buildAgentPrompt(agentName, selectedInput);

return {
optimizedInput: selectedInput,
systemPrompt: prompt.systemPrompt,
userPrompt: prompt.userPrompt,
model: config.model,

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

estimatedInputTokens is computed from compactInput (stringified compressedInput), but the actual prompt sent to the provider is buildAgentPrompt(...).userPrompt, which may include fewer keys and therefore be significantly smaller. This can incorrectly reduce remainingBudget / cap maxOutputTokens, or even fail-fast when the real prompt would fit. Consider estimating tokens from the final userPrompt (and ideally include systemPrompt + any fixed overhead) before deriving remainingBudget and compressionPasses telemetry.

Copilot uses AI. Check for mistakes.
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