mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
Default feedback export to telemetry.paperclip.ing
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
6940e3834b
commit
bbd759c0ff
3 files changed
+53
-5
No files matched your search
@@ -181,7 +181,7 @@ Votes you choose to share are sent to the Telemetry Backend immediately from the
|
||||
- App server responsibility: build the bundle, POST it to Telemetry Backend, update trace status
|
||||
- Telemetry Backend responsibility: authenticate the request, validate payload shape, compress/store the bundle, return the final object key
|
||||
- Retry behavior: failed uploads move to `failed` with an error message in `failureReason`, and the worker retries them on later ticks
|
||||
- Configuration requirement: remote export only works when `PAPERCLIP_FEEDBACK_EXPORT_BACKEND_URL` or `PAPERCLIP_TELEMETRY_BACKEND_URL` is configured on the app server
|
||||
- Default endpoint: when no feedback export backend URL is configured, Paperclip falls back to `https://telemetry.paperclip.ing`
|
||||
|
||||
Exported objects use a deterministic key pattern so they are easy to inspect:
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createFeedbackTraceShareClientFromConfig } from "../services/feedback-share-client.js";
|
||||
|
||||
describe("feedback trace share client", () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: vi.fn().mockResolvedValue({ objectKey: "feedback-traces/test.json" }),
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("defaults to telemetry.paperclip.ing when no backend url is configured", async () => {
|
||||
const client = createFeedbackTraceShareClientFromConfig({
|
||||
feedbackExportBackendUrl: undefined,
|
||||
feedbackExportBackendToken: undefined,
|
||||
});
|
||||
|
||||
await client.uploadTraceBundle({
|
||||
traceId: "trace-1",
|
||||
exportId: "export-1",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
issueIdentifier: "PAP-1",
|
||||
adapterType: "codex_local",
|
||||
captureStatus: "full",
|
||||
notes: [],
|
||||
envelope: {},
|
||||
surface: null,
|
||||
paperclipRun: null,
|
||||
rawAdapterTrace: null,
|
||||
normalizedAdapterTrace: null,
|
||||
privacy: null,
|
||||
integrity: {},
|
||||
files: [],
|
||||
});
|
||||
|
||||
expect(fetch).toHaveBeenCalledWith(
|
||||
"https://telemetry.paperclip.ing/feedback-traces",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { FeedbackTraceBundle } from "@paperclipai/shared";
|
||||
import type { Config } from "../config.js";
|
||||
|
||||
const DEFAULT_FEEDBACK_EXPORT_BACKEND_URL = "https://telemetry.paperclip.ing";
|
||||
|
||||
function buildFeedbackShareObjectKey(bundle: FeedbackTraceBundle, exportedAt: Date) {
|
||||
const year = String(exportedAt.getUTCFullYear());
|
||||
const month = String(exportedAt.getUTCMonth() + 1).padStart(2, "0");
|
||||
@@ -14,10 +16,8 @@ export interface FeedbackTraceShareClient {
|
||||
|
||||
export function createFeedbackTraceShareClientFromConfig(
|
||||
config: Pick<Config, "feedbackExportBackendUrl" | "feedbackExportBackendToken">,
|
||||
): FeedbackTraceShareClient | null {
|
||||
const baseUrl = config.feedbackExportBackendUrl?.trim();
|
||||
if (!baseUrl) return null;
|
||||
|
||||
): FeedbackTraceShareClient {
|
||||
const baseUrl = config.feedbackExportBackendUrl?.trim() || DEFAULT_FEEDBACK_EXPORT_BACKEND_URL;
|
||||
const token = config.feedbackExportBackendToken?.trim();
|
||||
const endpoint = new URL("/feedback-traces", baseUrl).toString();
|
||||
|
||||
|
||||
Reference in new issue
Block a user