mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 02:20:38 +09:00
Merge remote-tracking branch 'upstream/master' into feat/external-adapter-phase1
This commit is contained in:
commit
80b81459a7
10 changed files with 366 additions and 29 deletions
|
|
@ -1,6 +1,9 @@
|
|||
import { gzipSync } from "node:zlib";
|
||||
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 +17,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();
|
||||
|
||||
|
|
@ -25,6 +26,11 @@ export function createFeedbackTraceShareClientFromConfig(
|
|||
async uploadTraceBundle(bundle) {
|
||||
const exportedAt = new Date();
|
||||
const objectKey = buildFeedbackShareObjectKey(bundle, exportedAt);
|
||||
const requestBody = JSON.stringify({
|
||||
objectKey,
|
||||
exportedAt: exportedAt.toISOString(),
|
||||
bundle,
|
||||
});
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
|
@ -32,9 +38,8 @@ export function createFeedbackTraceShareClientFromConfig(
|
|||
...(token ? { authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
objectKey,
|
||||
exportedAt: exportedAt.toISOString(),
|
||||
bundle,
|
||||
encoding: "gzip+base64+json",
|
||||
payload: gzipSync(requestBody).toString("base64"),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ const MAX_SKILLS = 20;
|
|||
const MAX_INSTRUCTION_FILES = 20;
|
||||
const MAX_TRACE_FILE_CHARS = 10_000_000;
|
||||
const DEFAULT_INSTANCE_SETTINGS_SINGLETON_KEY = "default";
|
||||
const FEEDBACK_EXPORT_BACKEND_NOT_CONFIGURED = "Feedback export backend is not configured";
|
||||
|
||||
type FeedbackTraceRow = typeof feedbackExports.$inferSelect & {
|
||||
issueIdentifier: string | null;
|
||||
|
|
@ -1742,15 +1743,48 @@ export function feedbackService(db: Db, options: FeedbackServiceOptions = {}) {
|
|||
|
||||
flushPendingFeedbackTraces: async (input?: {
|
||||
companyId?: string;
|
||||
traceId?: string;
|
||||
limit?: number;
|
||||
now?: Date;
|
||||
}) => {
|
||||
const shareClient = options.shareClient;
|
||||
if (!shareClient) {
|
||||
const filters = [eq(feedbackExports.status, "pending")];
|
||||
if (input?.companyId) {
|
||||
filters.push(eq(feedbackExports.companyId, input.companyId));
|
||||
}
|
||||
if (input?.traceId) {
|
||||
filters.push(eq(feedbackExports.id, input.traceId));
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
id: feedbackExports.id,
|
||||
attemptCount: feedbackExports.attemptCount,
|
||||
})
|
||||
.from(feedbackExports)
|
||||
.where(and(...filters))
|
||||
.orderBy(asc(feedbackExports.createdAt), asc(feedbackExports.id))
|
||||
.limit(Math.max(1, Math.min(input?.limit ?? 25, 200)));
|
||||
|
||||
const attemptAt = input?.now ?? new Date();
|
||||
for (const row of rows) {
|
||||
await db
|
||||
.update(feedbackExports)
|
||||
.set({
|
||||
status: "failed",
|
||||
attemptCount: row.attemptCount + 1,
|
||||
lastAttemptedAt: attemptAt,
|
||||
failureReason: FEEDBACK_EXPORT_BACKEND_NOT_CONFIGURED,
|
||||
updatedAt: attemptAt,
|
||||
})
|
||||
.where(eq(feedbackExports.id, row.id));
|
||||
}
|
||||
|
||||
return {
|
||||
attempted: 0,
|
||||
attempted: rows.length,
|
||||
sent: 0,
|
||||
failed: 0,
|
||||
failed: rows.length,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1761,6 +1795,9 @@ export function feedbackService(db: Db, options: FeedbackServiceOptions = {}) {
|
|||
if (input?.companyId) {
|
||||
filters.push(eq(feedbackExports.companyId, input.companyId));
|
||||
}
|
||||
if (input?.traceId) {
|
||||
filters.push(eq(feedbackExports.id, input.traceId));
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
|
|
@ -1983,7 +2020,7 @@ export function feedbackService(db: Db, options: FeedbackServiceOptions = {}) {
|
|||
})
|
||||
.where(eq(feedbackVotes.id, savedVote.id));
|
||||
|
||||
await tx
|
||||
const [savedTrace] = await tx
|
||||
.insert(feedbackExports)
|
||||
.values({
|
||||
companyId: issue.companyId,
|
||||
|
|
@ -2030,6 +2067,9 @@ export function feedbackService(db: Db, options: FeedbackServiceOptions = {}) {
|
|||
failureReason: null,
|
||||
updatedAt: now,
|
||||
},
|
||||
})
|
||||
.returning({
|
||||
id: feedbackExports.id,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -2037,6 +2077,7 @@ export function feedbackService(db: Db, options: FeedbackServiceOptions = {}) {
|
|||
...savedVote,
|
||||
redactionSummary: artifacts.redactionSummary,
|
||||
},
|
||||
traceId: savedTrace?.id ?? null,
|
||||
consentEnabledNow,
|
||||
persistedSharingPreference,
|
||||
sharingEnabled: sharedWithLabs,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue