Skip to content

Commit 1226773

Browse files
authored
feat(daemon): route completed-run telemetry through Vela (#5666)
* feat(daemon): route completed-run telemetry through Vela * fix(daemon): migrate test telemetry relay domain * feat(daemon): send Vela feedback scores with completed-run telemetry Map score-create into the Vela envelope and use the same Vela sink for thumbs feedback, binding scores via the client run id instead of skipping them when account-aware telemetry is selected. * fix(daemon): skip anonymous fallback for Vela feedback auth errors Score-only feedback batches must stay on the account-scoped Vela path. When Vela returns 401/403, fail the delivery instead of posting to the anonymous relay, which has no matching trace for those scores. Generated-By: looper 0.11.0-beta.1 (runner=fixer, agent=grok-build)
1 parent 2d1f25a commit 1226773

9 files changed

Lines changed: 767 additions & 27 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const OPEN_DESIGN_TELEMETRY_RELAY_URLS = {
2+
test: 'https://telemetry-test.open-design.ai/api/langfuse',
3+
prod: 'https://telemetry.open-design.ai/api/langfuse',
4+
} as const;
5+
6+
const LEGACY_TEST_RELAY_ORIGIN = 'https://telemetry-selfhost.open-design.ai';
7+
const TEST_RELAY_ORIGIN = 'https://telemetry-test.open-design.ai';
8+
9+
/**
10+
* Keep legacy test configurations working while moving the test Worker to its
11+
* environment-owned hostname. Production and custom relay URLs are unchanged.
12+
*/
13+
export function normalizeOpenDesignTelemetryRelayUrl(value: string): string {
14+
const normalized = value.trim().replace(/\/+$/, '');
15+
return normalized.startsWith(`${LEGACY_TEST_RELAY_ORIGIN}/`) ||
16+
normalized === LEGACY_TEST_RELAY_ORIGIN
17+
? `${TEST_RELAY_ORIGIN}${normalized.slice(LEGACY_TEST_RELAY_ORIGIN.length)}`
18+
: normalized;
19+
}

apps/daemon/src/langfuse-bridge.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ import path from 'node:path';
1414

1515
import { modelIdForTracking } from '@open-design/contracts/analytics';
1616

17-
import { readAppConfig } from './app-config.js';
17+
import { agentCliEnvForAgent, readAppConfig } from './app-config.js';
1818
import type { AppVersionInfo } from './app-version.js';
1919
import { listMessages } from './db.js';
20+
import { normalizeOpenDesignTelemetryRelayUrl } from './integrations/telemetry-relay.js';
2021
import {
2122
deriveLangfuseDeliveryState,
22-
readTelemetrySinkConfig,
23+
readFeedbackTelemetrySinkConfig,
2324
reportRunCompleted,
2425
reportRunFeedback,
2526
type AgentEventSummary,
@@ -195,14 +196,21 @@ function inferObjectRegistrationRelayUrl(env: NodeJS.ProcessEnv = process.env):
195196
const objectRelayUrl = env.OPEN_DESIGN_OBJECT_RELAY_URL?.trim();
196197
if (!objectRelayUrl) {
197198
const telemetryRelayUrl = env.OPEN_DESIGN_TELEMETRY_RELAY_URL?.trim();
198-
return telemetryRelayUrl ? telemetryRelayUrl.replace(/\/+$/, '') : null;
199+
return telemetryRelayUrl
200+
? normalizeOpenDesignTelemetryRelayUrl(telemetryRelayUrl)
201+
: null;
199202
}
203+
const normalizedObjectRelayUrl = normalizeOpenDesignTelemetryRelayUrl(
204+
objectRelayUrl,
205+
);
200206
try {
201-
const url = new URL(objectRelayUrl);
207+
const url = new URL(normalizedObjectRelayUrl);
202208
url.pathname = url.pathname.replace(/\/api\/objects\/batch\/?$/, '/api/langfuse');
203209
return url.toString().replace(/\/+$/, '');
204210
} catch {
205-
return objectRelayUrl.replace(/\/api\/objects\/batch\/?$/, '/api/langfuse').replace(/\/+$/, '');
211+
return normalizedObjectRelayUrl
212+
.replace(/\/api\/objects\/batch\/?$/, '/api/langfuse')
213+
.replace(/\/+$/, '');
206214
}
207215
}
208216

@@ -949,6 +957,7 @@ export async function reportRunCompletedFromDaemon(
949957
return deriveLangfuseDeliveryState(prefs, null);
950958
}
951959
const installationId = cfg.installationId ?? null;
960+
const configuredAmrEnv = agentCliEnvForAgent(cfg.agentCliEnv, 'amr');
952961

953962
let messageContent = '';
954963
let producedFilesRaw: unknown = undefined;
@@ -1133,6 +1142,7 @@ export async function reportRunCompletedFromDaemon(
11331142
buildContext(mergeTraceSafeManifests(manifests, registrationManifests)),
11341143
{
11351144
config: objectRegistrationTelemetryConfig(),
1145+
deliveryPurpose: 'object-registration',
11361146
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
11371147
},
11381148
);
@@ -1145,7 +1155,10 @@ export async function reportRunCompletedFromDaemon(
11451155
traceObjectFilesRaw,
11461156
...(uploadedManifests ? { uploaded: uploadedManifests } : {}),
11471157
})),
1148-
opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
1158+
{
1159+
configuredEnv: configuredAmrEnv,
1160+
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
1161+
},
11491162
);
11501163
} catch (err) {
11511164
console.warn('[langfuse-bridge] report failed:', String(err));
@@ -1198,7 +1211,8 @@ export async function reportRunFeedbackFromDaemon(
11981211
// Pre-resolve the sink before claiming `accepted`. Avoids advertising a
11991212
// successful enqueue to callers when there's no Langfuse endpoint
12001213
// configured to ship the score to.
1201-
const sink = readTelemetrySinkConfig();
1214+
const configuredAmrEnv = agentCliEnvForAgent(cfg.agentCliEnv, 'amr');
1215+
const sink = readFeedbackTelemetrySinkConfig(process.env, configuredAmrEnv);
12021216
if (!sink) {
12031217
return { status: 'skipped_no_sink' };
12041218
}
@@ -1218,7 +1232,10 @@ export async function reportRunFeedbackFromDaemon(
12181232
// telemetry, not a client-facing signal.
12191233
void reportRunFeedback(
12201234
ctx,
1221-
opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {},
1235+
{
1236+
configuredEnv: configuredAmrEnv,
1237+
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
1238+
},
12221239
).catch((err) => {
12231240
console.warn('[langfuse-bridge] feedback report failed:', String(err));
12241241
});

0 commit comments

Comments
 (0)