feat: Python otel support, enriching spans, OpenAI Agents SDK example (#1839)
* D3 demo WIP * Agent working and completing token successfully * Sending OpenAI Agent SDK spans through to the platform now works * A couple of perf tweaks for enriching events
@@ -27,6 +27,7 @@ import {
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { trace, Tracer } from "@opentelemetry/api";
|
||||
import { startSpan } from "./tracing.server";
|
||||
import { enrichCreatableEvents } from "./utils/enrichCreatableEvents.server";
|
||||
|
||||
export type OTLPExporterConfig = {
|
||||
batchSize: number;
|
||||
@@ -54,14 +55,16 @@ class OTLPExporter {
|
||||
return convertSpansToCreateableEvents(resourceSpan);
|
||||
});
|
||||
|
||||
this.#logEventsVerbose(events);
|
||||
const enrichedEvents = enrichCreatableEvents(events);
|
||||
|
||||
span.setAttribute("event_count", events.length);
|
||||
this.#logEventsVerbose(enrichedEvents);
|
||||
|
||||
span.setAttribute("event_count", enrichedEvents.length);
|
||||
|
||||
if (immediate) {
|
||||
await this._eventRepository.insertManyImmediate(events);
|
||||
await this._eventRepository.insertManyImmediate(enrichedEvents);
|
||||
} else {
|
||||
await this._eventRepository.insertMany(events);
|
||||
await this._eventRepository.insertMany(enrichedEvents);
|
||||
}
|
||||
|
||||
return ExportTraceServiceResponse.create();
|
||||
@@ -79,14 +82,16 @@ class OTLPExporter {
|
||||
return convertLogsToCreateableEvents(resourceLog);
|
||||
});
|
||||
|
||||
this.#logEventsVerbose(events);
|
||||
const enrichedEvents = enrichCreatableEvents(events);
|
||||
|
||||
span.setAttribute("event_count", events.length);
|
||||
this.#logEventsVerbose(enrichedEvents);
|
||||
|
||||
span.setAttribute("event_count", enrichedEvents.length);
|
||||
|
||||
if (immediate) {
|
||||
await this._eventRepository.insertManyImmediate(events);
|
||||
await this._eventRepository.insertManyImmediate(enrichedEvents);
|
||||
} else {
|
||||
await this._eventRepository.insertMany(events);
|
||||
await this._eventRepository.insertMany(enrichedEvents);
|
||||
}
|
||||
|
||||
return ExportLogsServiceResponse.create();
|
||||
@@ -135,16 +140,28 @@ class OTLPExporter {
|
||||
(attribute) => attribute.key === SemanticInternalAttributes.TRIGGER
|
||||
);
|
||||
|
||||
if (!triggerAttribute) {
|
||||
const executionEnvironmentAttribute = resourceSpan.resource?.attributes.find(
|
||||
(attribute) => attribute.key === SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
);
|
||||
|
||||
if (!triggerAttribute && !executionEnvironmentAttribute) {
|
||||
logger.debug("Skipping resource span without trigger attribute", {
|
||||
attributes: resourceSpan.resource?.attributes,
|
||||
spans: resourceSpan.scopeSpans.flatMap((scopeSpan) => scopeSpan.spans),
|
||||
});
|
||||
|
||||
return;
|
||||
return true; // go ahead and let this resource span through
|
||||
}
|
||||
|
||||
return isBoolValue(triggerAttribute.value) ? triggerAttribute.value.boolValue : false;
|
||||
const executionEnvironment = isStringValue(executionEnvironmentAttribute?.value)
|
||||
? executionEnvironmentAttribute.value.stringValue
|
||||
: undefined;
|
||||
|
||||
if (executionEnvironment === "trigger") {
|
||||
return true; // go ahead and let this resource span through
|
||||
}
|
||||
|
||||
return isBoolValue(triggerAttribute?.value) ? triggerAttribute.value.boolValue : false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { CreatableEvent } from "../eventRepository.server";
|
||||
|
||||
export function enrichCreatableEvents(events: CreatableEvent[]) {
|
||||
return events.map((event) => {
|
||||
return enrichCreatableEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
function enrichCreatableEvent(event: CreatableEvent): CreatableEvent {
|
||||
const message = formatPythonStyle(event.message, event.properties);
|
||||
|
||||
event.message = message;
|
||||
event.style = enrichStyle(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
function enrichStyle(event: CreatableEvent) {
|
||||
const baseStyle = event.style ?? {};
|
||||
const props = event.properties;
|
||||
|
||||
// Direct property access and early returns
|
||||
// GenAI System check
|
||||
const system = props["gen_ai.system"];
|
||||
if (typeof system === "string") {
|
||||
return { ...baseStyle, icon: `tabler-brand-${system}` };
|
||||
}
|
||||
|
||||
// Agent workflow check
|
||||
const name = props["name"];
|
||||
if (typeof name === "string" && name.includes("Agent workflow")) {
|
||||
return { ...baseStyle, icon: "tabler-brain" };
|
||||
}
|
||||
|
||||
return baseStyle;
|
||||
}
|
||||
|
||||
function repr(value: any): string {
|
||||
if (typeof value === "string") {
|
||||
return `'${value}'`;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function formatPythonStyle(template: string, values: Record<string, any>): string {
|
||||
// Early return if template is too long
|
||||
if (template.length >= 256) {
|
||||
return template;
|
||||
}
|
||||
|
||||
// Early return if no template variables present
|
||||
if (!template.includes("{")) {
|
||||
return template;
|
||||
}
|
||||
|
||||
return template.replace(/\{([^}]+?)(?:!r)?\}/g, (match, key) => {
|
||||
const hasRepr = match.endsWith("!r}");
|
||||
const actualKey = hasRepr ? key : key;
|
||||
const value = values[actualKey];
|
||||
|
||||
if (value === undefined) {
|
||||
return match;
|
||||
}
|
||||
|
||||
return hasRepr ? repr(value) : String(value);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { enrichCreatableEvents } from "../app/v3/utils/enrichCreatableEvents.server.js";
|
||||
import {
|
||||
RuntimeEnvironmentType,
|
||||
TaskEventKind,
|
||||
TaskEventLevel,
|
||||
TaskEventStatus,
|
||||
} from "@trigger.dev/database";
|
||||
import { SemanticInternalAttributes } from "@trigger.dev/core/v3";
|
||||
|
||||
describe("OTLPExporter", () => {
|
||||
describe("enrichCreatableEvents", () => {
|
||||
it("should enrich creatable events that have template strings in the message", () => {
|
||||
const events = [
|
||||
{
|
||||
traceId: "4e184936f7a0ef5af428a29b4d47da9d",
|
||||
spanId: "1be4ebd8c78e43e9",
|
||||
parentId: "f1a6d77fd5872bbe",
|
||||
message: "Responses API with {gen_ai.request.model!r}",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1743103947959190000),
|
||||
links: [],
|
||||
events: [],
|
||||
duration: 2931702000,
|
||||
properties: {
|
||||
"code.filepath": "src/trigger/python/agent.py",
|
||||
"code.lineno": 171,
|
||||
model_settings:
|
||||
'{"temperature":null,"top_p":null,"frequency_penalty":null,"presence_penalty":null,"tool_choice":null,"parallel_tool_calls":false,"truncation":null,"max_tokens":null}',
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"logfire.msg_template": "Responses API with {gen_ai.request.model!r}",
|
||||
"logfire.span_type": "span",
|
||||
response_id: "resp_67e5a7cd046c8192b3bfb6c863057f60003e29fb266e3c7b",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.response.model": "gpt-4o-2024-08-06",
|
||||
response:
|
||||
'{"id":"resp_67e5a7cd046c8192b3bfb6c863057f60003e29fb266e3c7b","created_at":1743103949.0,"error":null,"incomplete_details":null,"instructions":"You are an expert at enriching contact information data.\\n Your task is to use web search to find additional information about a person\\n based on their basic contact details.\\n\\n Please find the basic information, company information, and social media information for the person.\\n\\n The input data is from an unspecified CSV dataset, but most likely a CSV dump from a CRM or other source like Mailchimp, etc.\\n\\n You\'ll receive the header row and a single row from the dataset to enrich, in their raw form.\\n \\n Only include information you are confident about from reliable sources.\\n Use null for fields where you cannot find reliable information.\\n ","metadata":{},"model":"gpt-4o-2024-08-06","object":"response","output":[{"arguments":"{\\"input\\":\\"Eric,eric@trigger.dev\\"}","call_id":"call_OPlvy1ubdx75L8AgGIfVTWlW","name":"basic_info_agent","type":"function_call","id":"fc_67e5a7ce1418819281d73995bf0ffcf9003e29fb266e3c7b","status":"completed"}],"parallel_tool_calls":false,"temperature":1.0,"tool_choice":"auto","tools":[{"name":"basic_info_agent","parameters":{"properties":{"input":{"title":"Input","type":"string"}},"required":["input"],"title":"basic_info_agent_args","type":"object","additionalProperties":false},"strict":true,"type":"function","description":"Extract basic information from a person\'s contact details"},{"name":"company_info_agent","parameters":{"properties":{"input":{"title":"Input","type":"string"}},"required":["input"],"title":"company_info_agent_args","type":"object","additionalProperties":false},"strict":true,"type":"function","description":"Extract company information from a person\'s contact details"},{"name":"social_info_agent","parameters":{"properties":{"input":{"title":"Input","type":"string"}},"required":["input"],"title":"social_info_agent_args","type":"object","additionalProperties":false},"strict":true,"type":"function","description":"Extract social media information from a person\'s contact details"}],"top_p":1.0,"max_output_tokens":null,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"status":"completed","text":{"format":{"schema_":{"$defs":{"BasicInfo":{"properties":{"name":{"description":"The name of the row","title":"Name","type":"string"},"email":{"description":"The email of the row","title":"Email","type":"string"},"firstName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first name of the row","title":"Firstname"},"lastName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The last name of the row","title":"Lastname"},"preferredName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The preferred name of the row","title":"Preferredname"}},"required":["name","email","firstName","lastName","preferredName"],"title":"BasicInfo","type":"object","additionalProperties":false},"CompanyInfo":{"properties":{"name":{"description":"The name of the company","title":"Name","type":"string"},"industry":{"description":"The industry of the company","title":"Industry","type":"string"}},"required":["name","industry"],"title":"CompanyInfo","type":"object","additionalProperties":false},"SocialInfo":{"properties":{"twitter":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Twitter username of the person","title":"Twitter"},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The LinkedIn username of the person","title":"Linkedin"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Facebook username of the person","title":"Facebook"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Instagram username of the person","title":"Instagram"}},"title":"SocialInfo","type":"object","additionalProperties":false,"required":["twitter","linkedin","facebook","instagram"]}},"properties":{"basicInfo":{"$ref":"#/$defs/BasicInfo"},"companyInfo":{"$ref":"#/$defs/CompanyInfo"},"socialInfo":{"$ref":"#/$defs/SocialInfo"}},"required":["basicInfo","companyInfo","socialInfo"],"title":"RowEnrichmentResult","type":"object","additionalProperties":false},"type":"json_schema","description":null,"name":"final_output","strict":true}},"truncation":"disabled","usage":{"input_tokens":702,"input_tokens_details":{"cached_tokens":0},"output_tokens":22,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":724},"user":null,"store":true}',
|
||||
"gen_ai.operation.name": "chat",
|
||||
raw_input:
|
||||
'[{"content":"\\n Header row: Name,Email\\n Row to enrich: Eric,eric@trigger.dev\\n CSV URL: https://example.com/import.csv\\n ","role":"user"}]',
|
||||
events:
|
||||
'[{"event.name":"gen_ai.system.message","content":"You are an expert at enriching contact information data.\\n Your task is to use web search to find additional information about a person\\n based on their basic contact details.\\n\\n Please find the basic information, company information, and social media information for the person.\\n\\n The input data is from an unspecified CSV dataset, but most likely a CSV dump from a CRM or other source like Mailchimp, etc.\\n\\n You\'ll receive the header row and a single row from the dataset to enrich, in their raw form.\\n \\n Only include information you are confident about from reliable sources.\\n Use null for fields where you cannot find reliable information.\\n ","role":"system"},{"event.name":"gen_ai.user.message","content":"\\n Header row: Name,Email\\n Row to enrich: Eric,eric@trigger.dev\\n CSV URL: https://example.com/import.csv\\n ","role":"user"},{"event.name":"gen_ai.choice","index":0,"message":{"role":"assistant","tool_calls":[{"id":"call_OPlvy1ubdx75L8AgGIfVTWlW","type":"function","function":{"name":"basic_info_agent","arguments":"{\\"input\\":\\"Eric,eric@trigger.dev\\"}"}}]}}]',
|
||||
"gen_ai.usage.input_tokens": 702,
|
||||
"gen_ai.usage.output_tokens": 22,
|
||||
"logfire.msg": "Responses API with 'gpt-4o'",
|
||||
},
|
||||
payloadType: "application/json",
|
||||
metadata: {
|
||||
"service.instance.id": "eae3f05bfa924058b1aa79c6decd79fe",
|
||||
},
|
||||
serviceName: "d3-demo",
|
||||
serviceNamespace: "unknown",
|
||||
environmentId: "cm8r871mw0002dy8n6ec3y0i2",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
organizationId: "cm5ohng6o0002dyuzvr0qq4xo",
|
||||
projectId: "cm8r871lq0000dy8nhljain0w",
|
||||
projectRef: "proj_wkbbtayxrmeyqhankehb",
|
||||
runId: "run_cm8rr2zw50000efdyyq0376m5",
|
||||
runIsTest: false,
|
||||
attemptId: "deprecated",
|
||||
taskSlug: "row-agent-runner",
|
||||
taskPath: "src/trigger/d3Demo.ts",
|
||||
taskExportName: "@deprecated",
|
||||
workerId: "unmanaged",
|
||||
workerVersion: "20250327.54",
|
||||
queueId: "queue_pn4256pg6c2wc9rro3o7i",
|
||||
queueName: "task/row-agent-runner",
|
||||
batchId: "undefined",
|
||||
idempotencyKey: "undefined",
|
||||
machinePreset: "small-1x",
|
||||
style: {},
|
||||
output: {},
|
||||
payload: {},
|
||||
},
|
||||
];
|
||||
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events).toBeDefined();
|
||||
|
||||
const event = $events[0];
|
||||
expect(event.message).toBe("Responses API with 'gpt-4o'");
|
||||
expect(event.style).toEqual({
|
||||
icon: "tabler-brand-openai",
|
||||
});
|
||||
});
|
||||
|
||||
it("should enrich creatable events that have template strings without the !r in the message", () => {
|
||||
const events = [
|
||||
{
|
||||
traceId: "4e184936f7a0ef5af428a29b4d47da9d",
|
||||
spanId: "1be4ebd8c78e43e9",
|
||||
parentId: "f1a6d77fd5872bbe",
|
||||
message: "Responses API with {gen_ai.request.model}",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1743103947959190000),
|
||||
links: [],
|
||||
events: [],
|
||||
duration: 2931702000,
|
||||
properties: {
|
||||
"code.filepath": "src/trigger/python/agent.py",
|
||||
"code.lineno": 171,
|
||||
model_settings:
|
||||
'{"temperature":null,"top_p":null,"frequency_penalty":null,"presence_penalty":null,"tool_choice":null,"parallel_tool_calls":false,"truncation":null,"max_tokens":null}',
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"logfire.msg_template": "Responses API with {gen_ai.request.model}",
|
||||
"logfire.span_type": "span",
|
||||
response_id: "resp_67e5a7cd046c8192b3bfb6c863057f60003e29fb266e3c7b",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.response.model": "gpt-4o-2024-08-06",
|
||||
response:
|
||||
'{"id":"resp_67e5a7cd046c8192b3bfb6c863057f60003e29fb266e3c7b","created_at":1743103949.0,"error":null,"incomplete_details":null,"instructions":"You are an expert at enriching contact information data.\\n Your task is to use web search to find additional information about a person\\n based on their basic contact details.\\n\\n Please find the basic information, company information, and social media information for the person.\\n\\n The input data is from an unspecified CSV dataset, but most likely a CSV dump from a CRM or other source like Mailchimp, etc.\\n\\n You\'ll receive the header row and a single row from the dataset to enrich, in their raw form.\\n \\n Only include information you are confident about from reliable sources.\\n Use null for fields where you cannot find reliable information.\\n ","metadata":{},"model":"gpt-4o-2024-08-06","object":"response","output":[{"arguments":"{\\"input\\":\\"Eric,eric@trigger.dev\\"}","call_id":"call_OPlvy1ubdx75L8AgGIfVTWlW","name":"basic_info_agent","type":"function_call","id":"fc_67e5a7ce1418819281d73995bf0ffcf9003e29fb266e3c7b","status":"completed"}],"parallel_tool_calls":false,"temperature":1.0,"tool_choice":"auto","tools":[{"name":"basic_info_agent","parameters":{"properties":{"input":{"title":"Input","type":"string"}},"required":["input"],"title":"basic_info_agent_args","type":"object","additionalProperties":false},"strict":true,"type":"function","description":"Extract basic information from a person\'s contact details"},{"name":"company_info_agent","parameters":{"properties":{"input":{"title":"Input","type":"string"}},"required":["input"],"title":"company_info_agent_args","type":"object","additionalProperties":false},"strict":true,"type":"function","description":"Extract company information from a person\'s contact details"},{"name":"social_info_agent","parameters":{"properties":{"input":{"title":"Input","type":"string"}},"required":["input"],"title":"social_info_agent_args","type":"object","additionalProperties":false},"strict":true,"type":"function","description":"Extract social media information from a person\'s contact details"}],"top_p":1.0,"max_output_tokens":null,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"status":"completed","text":{"format":{"schema_":{"$defs":{"BasicInfo":{"properties":{"name":{"description":"The name of the row","title":"Name","type":"string"},"email":{"description":"The email of the row","title":"Email","type":"string"},"firstName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first name of the row","title":"Firstname"},"lastName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The last name of the row","title":"Lastname"},"preferredName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The preferred name of the row","title":"Preferredname"}},"required":["name","email","firstName","lastName","preferredName"],"title":"BasicInfo","type":"object","additionalProperties":false},"CompanyInfo":{"properties":{"name":{"description":"The name of the company","title":"Name","type":"string"},"industry":{"description":"The industry of the company","title":"Industry","type":"string"}},"required":["name","industry"],"title":"CompanyInfo","type":"object","additionalProperties":false},"SocialInfo":{"properties":{"twitter":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Twitter username of the person","title":"Twitter"},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The LinkedIn username of the person","title":"Linkedin"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Facebook username of the person","title":"Facebook"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Instagram username of the person","title":"Instagram"}},"title":"SocialInfo","type":"object","additionalProperties":false,"required":["twitter","linkedin","facebook","instagram"]}},"properties":{"basicInfo":{"$ref":"#/$defs/BasicInfo"},"companyInfo":{"$ref":"#/$defs/CompanyInfo"},"socialInfo":{"$ref":"#/$defs/SocialInfo"}},"required":["basicInfo","companyInfo","socialInfo"],"title":"RowEnrichmentResult","type":"object","additionalProperties":false},"type":"json_schema","description":null,"name":"final_output","strict":true}},"truncation":"disabled","usage":{"input_tokens":702,"input_tokens_details":{"cached_tokens":0},"output_tokens":22,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":724},"user":null,"store":true}',
|
||||
"gen_ai.operation.name": "chat",
|
||||
raw_input:
|
||||
'[{"content":"\\n Header row: Name,Email\\n Row to enrich: Eric,eric@trigger.dev\\n CSV URL: https://example.com/import.csv\\n ","role":"user"}]',
|
||||
events:
|
||||
'[{"event.name":"gen_ai.system.message","content":"You are an expert at enriching contact information data.\\n Your task is to use web search to find additional information about a person\\n based on their basic contact details.\\n\\n Please find the basic information, company information, and social media information for the person.\\n\\n The input data is from an unspecified CSV dataset, but most likely a CSV dump from a CRM or other source like Mailchimp, etc.\\n\\n You\'ll receive the header row and a single row from the dataset to enrich, in their raw form.\\n \\n Only include information you are confident about from reliable sources.\\n Use null for fields where you cannot find reliable information.\\n ","role":"system"},{"event.name":"gen_ai.user.message","content":"\\n Header row: Name,Email\\n Row to enrich: Eric,eric@trigger.dev\\n CSV URL: https://example.com/import.csv\\n ","role":"user"},{"event.name":"gen_ai.choice","index":0,"message":{"role":"assistant","tool_calls":[{"id":"call_OPlvy1ubdx75L8AgGIfVTWlW","type":"function","function":{"name":"basic_info_agent","arguments":"{\\"input\\":\\"Eric,eric@trigger.dev\\"}"}}]}}]',
|
||||
"gen_ai.usage.input_tokens": 702,
|
||||
"gen_ai.usage.output_tokens": 22,
|
||||
"logfire.msg": "Responses API with 'gpt-4o'",
|
||||
},
|
||||
payloadType: "application/json",
|
||||
metadata: {
|
||||
"service.instance.id": "eae3f05bfa924058b1aa79c6decd79fe",
|
||||
},
|
||||
serviceName: "d3-demo",
|
||||
serviceNamespace: "unknown",
|
||||
environmentId: "cm8r871mw0002dy8n6ec3y0i2",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
organizationId: "cm5ohng6o0002dyuzvr0qq4xo",
|
||||
projectId: "cm8r871lq0000dy8nhljain0w",
|
||||
projectRef: "proj_wkbbtayxrmeyqhankehb",
|
||||
runId: "run_cm8rr2zw50000efdyyq0376m5",
|
||||
runIsTest: false,
|
||||
attemptId: "deprecated",
|
||||
taskSlug: "row-agent-runner",
|
||||
taskPath: "src/trigger/d3Demo.ts",
|
||||
taskExportName: "@deprecated",
|
||||
workerId: "unmanaged",
|
||||
workerVersion: "20250327.54",
|
||||
queueId: "queue_pn4256pg6c2wc9rro3o7i",
|
||||
queueName: "task/row-agent-runner",
|
||||
batchId: "undefined",
|
||||
idempotencyKey: "undefined",
|
||||
machinePreset: "small-1x",
|
||||
style: {},
|
||||
output: {},
|
||||
payload: {},
|
||||
},
|
||||
];
|
||||
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events).toBeDefined();
|
||||
|
||||
const event = $events[0];
|
||||
expect(event.message).toBe("Responses API with gpt-4o");
|
||||
expect(event.style).toEqual({
|
||||
icon: "tabler-brand-openai",
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle missing properties gracefully", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Responses API with {missing.property!r}",
|
||||
properties: {},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: {},
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].message).toBe("Responses API with {missing.property!r}");
|
||||
expect($events[0].style).toEqual({});
|
||||
});
|
||||
|
||||
it("should handle multiple template variables in one message", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Using {gen_ai.request.model!r} with temperature {gen_ai.request.temperature}",
|
||||
properties: {
|
||||
"gen_ai.request.model": "gpt-4",
|
||||
"gen_ai.request.temperature": 0.7,
|
||||
"gen_ai.system": "openai",
|
||||
},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: {},
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].message).toBe("Using 'gpt-4' with temperature 0.7");
|
||||
expect($events[0].style).toEqual({
|
||||
icon: "tabler-brand-openai",
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle non-string property values", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Count is {count!r} and enabled is {enabled!r}",
|
||||
properties: {
|
||||
count: 42,
|
||||
enabled: true,
|
||||
"gen_ai.system": "anthropic",
|
||||
},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: {},
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].message).toBe("Count is 42 and enabled is true");
|
||||
expect($events[0].style).toEqual({
|
||||
icon: "tabler-brand-anthropic",
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle messages without any template variables", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Plain message without variables",
|
||||
properties: {
|
||||
"gen_ai.system": "openai",
|
||||
},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: {},
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].message).toBe("Plain message without variables");
|
||||
expect($events[0].style).toEqual({
|
||||
icon: "tabler-brand-openai",
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle style enrichment without gen_ai.system", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Message without system",
|
||||
properties: {
|
||||
"some.other.property": "value",
|
||||
},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: { existingStyle: "value" },
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].style).toEqual({ existingStyle: "value" });
|
||||
});
|
||||
|
||||
it("should apply the first matching style enricher", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Agent workflow",
|
||||
properties: {
|
||||
"gen_ai.system": "openai",
|
||||
name: "Agent workflow",
|
||||
},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false, // This would match the error enricher, but GenAI comes first
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: { existingStyle: "value" },
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].style).toEqual({
|
||||
existingStyle: "value",
|
||||
icon: "tabler-brand-openai", // GenAI enricher wins because it's first
|
||||
});
|
||||
});
|
||||
|
||||
it("should fall back to next enricher if first doesn't match", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Agent workflow",
|
||||
properties: {
|
||||
"task.type": "background",
|
||||
name: "Agent workflow",
|
||||
},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: true,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: { existingStyle: "value" },
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].style).toEqual({
|
||||
existingStyle: "value",
|
||||
icon: "tabler-brain",
|
||||
});
|
||||
});
|
||||
|
||||
it("should preserve existing style if no enricher matches", () => {
|
||||
const events = [
|
||||
{
|
||||
message: "Test message",
|
||||
properties: {},
|
||||
traceId: "test",
|
||||
spanId: "test",
|
||||
isPartial: false,
|
||||
isError: false,
|
||||
kind: TaskEventKind.INTERNAL,
|
||||
level: TaskEventLevel.TRACE,
|
||||
status: TaskEventStatus.UNSET,
|
||||
startTime: BigInt(1),
|
||||
style: { existingStyle: "value" },
|
||||
serviceName: "test",
|
||||
environmentType: RuntimeEnvironmentType.DEVELOPMENT,
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-expect-error
|
||||
const $events = enrichCreatableEvents(events);
|
||||
expect($events[0].style).toEqual({
|
||||
existingStyle: "value",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Span, SpanStatusCode } from "@opentelemetry/api";
|
||||
import { type Span, SpanStatusCode, context, propagation } from "@opentelemetry/api";
|
||||
|
||||
export function recordSpanException(span: Span, error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
@@ -20,3 +20,10 @@ function sanitizeSpanError(error: Error) {
|
||||
|
||||
return sanitizedError;
|
||||
}
|
||||
|
||||
export function carrierFromContext(): Record<string, string> {
|
||||
const carrier = {};
|
||||
propagation.inject(context.active(), carrier);
|
||||
|
||||
return carrier;
|
||||
}
|
||||
|
||||
@@ -57,4 +57,5 @@ export const SemanticInternalAttributes = {
|
||||
RATE_LIMIT_RESET: "response.rateLimit.reset",
|
||||
SPAN_ATTEMPT: "$span.attempt",
|
||||
METRIC_EVENTS: "$metrics.events",
|
||||
EXECUTION_ENVIRONMENT: "exec_env",
|
||||
};
|
||||
|
||||
@@ -4,7 +4,12 @@ export { PreciseWallClock as DurableClock } from "../clock/preciseWallClock.js";
|
||||
export { getEnvVar, getNumberEnvVar } from "../utils/getEnv.js";
|
||||
export { OtelTaskLogger, logLevels } from "../logger/taskLogger.js";
|
||||
export { ConsoleInterceptor } from "../consoleInterceptor.js";
|
||||
export { TracingSDK, type TracingDiagnosticLogLevel, recordSpanException } from "../otel/index.js";
|
||||
export {
|
||||
TracingSDK,
|
||||
type TracingDiagnosticLogLevel,
|
||||
recordSpanException,
|
||||
carrierFromContext,
|
||||
} from "../otel/index.js";
|
||||
export { StandardResourceCatalog } from "../resource-catalog/standardResourceCatalog.js";
|
||||
export {
|
||||
TaskContextSpanProcessor,
|
||||
|
||||
@@ -2,8 +2,10 @@ import {
|
||||
AsyncIterableStream,
|
||||
createAsyncIterableStreamFromAsyncIterable,
|
||||
SemanticInternalAttributes,
|
||||
taskContext,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { logger } from "@trigger.dev/sdk/v3";
|
||||
import { carrierFromContext } from "@trigger.dev/core/v3/otel";
|
||||
import assert from "node:assert";
|
||||
import fs from "node:fs";
|
||||
import { Result, x, Options as XOptions } from "tinyexec";
|
||||
@@ -17,6 +19,8 @@ export const python = {
|
||||
async run(scriptArgs: string[] = [], options: PythonExecOptions = {}): Promise<Result> {
|
||||
const pythonBin = process.env.PYTHON_BIN_PATH || "python";
|
||||
|
||||
const carrier = carrierFromContext();
|
||||
|
||||
return await logger.trace(
|
||||
"python.run()",
|
||||
async (span) => {
|
||||
@@ -27,6 +31,12 @@ export const python = {
|
||||
env: {
|
||||
...process.env,
|
||||
...options.env,
|
||||
TRACEPARENT: carrier["traceparent"],
|
||||
OTEL_RESOURCE_ATTRIBUTES: `${
|
||||
SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
}=trigger,${Object.entries(taskContext.attributes)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",")}`,
|
||||
},
|
||||
},
|
||||
throwOnError: false, // Ensure errors are handled manually
|
||||
@@ -50,7 +60,7 @@ export const python = {
|
||||
attributes: {
|
||||
pythonBin,
|
||||
args: scriptArgs.join(" "),
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "brand-python",
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "python",
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -69,6 +79,8 @@ export const python = {
|
||||
async (span) => {
|
||||
span.setAttribute("scriptPath", scriptPath);
|
||||
|
||||
const carrier = carrierFromContext();
|
||||
|
||||
const result = await x(
|
||||
process.env.PYTHON_BIN_PATH || "python",
|
||||
[scriptPath, ...scriptArgs],
|
||||
@@ -79,6 +91,13 @@ export const python = {
|
||||
env: {
|
||||
...process.env,
|
||||
...options.env,
|
||||
TRACEPARENT: carrier["traceparent"],
|
||||
OTEL_RESOURCE_ATTRIBUTES: `${
|
||||
SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
}=trigger,${Object.entries(taskContext.attributes)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",")}`,
|
||||
OTEL_LOG_LEVEL: "DEBUG",
|
||||
},
|
||||
},
|
||||
throwOnError: false,
|
||||
@@ -93,7 +112,7 @@ export const python = {
|
||||
throw new Error(
|
||||
`${scriptPath} ${scriptArgs.join(" ")} exited with a non-zero code ${
|
||||
result.exitCode
|
||||
}:\n${result.stderr}`
|
||||
}:\n${result.stdout}\n${result.stderr}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,7 +123,7 @@ export const python = {
|
||||
pythonBin: process.env.PYTHON_BIN_PATH || "python",
|
||||
scriptPath,
|
||||
args: scriptArgs.join(" "),
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "brand-python",
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "python",
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -124,6 +143,8 @@ export const python = {
|
||||
async (tempFilePath) => {
|
||||
span.setAttribute("tempFilePath", tempFilePath);
|
||||
|
||||
const carrier = carrierFromContext();
|
||||
|
||||
const pythonBin = process.env.PYTHON_BIN_PATH || "python";
|
||||
const result = await x(pythonBin, [tempFilePath], {
|
||||
...options,
|
||||
@@ -132,6 +153,12 @@ export const python = {
|
||||
env: {
|
||||
...process.env,
|
||||
...options.env,
|
||||
TRACEPARENT: carrier["traceparent"],
|
||||
OTEL_RESOURCE_ATTRIBUTES: `${
|
||||
SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
}=trigger,${Object.entries(taskContext.attributes)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",")}`,
|
||||
},
|
||||
},
|
||||
throwOnError: false,
|
||||
@@ -157,7 +184,7 @@ export const python = {
|
||||
pythonBin: process.env.PYTHON_BIN_PATH || "python",
|
||||
contentPreview:
|
||||
scriptContent.substring(0, 100) + (scriptContent.length > 100 ? "..." : ""),
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "brand-python",
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "python",
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -167,6 +194,8 @@ export const python = {
|
||||
run(scriptArgs: string[] = [], options: PythonExecOptions = {}): AsyncIterableStream<string> {
|
||||
const pythonBin = process.env.PYTHON_BIN_PATH || "python";
|
||||
|
||||
const carrier = carrierFromContext();
|
||||
|
||||
const pythonProcess = x(pythonBin, scriptArgs, {
|
||||
...options,
|
||||
nodeOptions: {
|
||||
@@ -174,6 +203,12 @@ export const python = {
|
||||
env: {
|
||||
...process.env,
|
||||
...options.env,
|
||||
TRACEPARENT: carrier["traceparent"],
|
||||
OTEL_RESOURCE_ATTRIBUTES: `${
|
||||
SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
}=trigger,${Object.entries(taskContext.attributes)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",")}`,
|
||||
},
|
||||
},
|
||||
throwOnError: false,
|
||||
@@ -183,7 +218,7 @@ export const python = {
|
||||
attributes: {
|
||||
pythonBin,
|
||||
args: scriptArgs.join(" "),
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "brand-python",
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "python",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -206,6 +241,8 @@ export const python = {
|
||||
|
||||
const pythonBin = process.env.PYTHON_BIN_PATH || "python";
|
||||
|
||||
const carrier = carrierFromContext();
|
||||
|
||||
const pythonProcess = x(pythonBin, [scriptPath, ...scriptArgs], {
|
||||
...options,
|
||||
nodeOptions: {
|
||||
@@ -213,6 +250,12 @@ export const python = {
|
||||
env: {
|
||||
...process.env,
|
||||
...options.env,
|
||||
TRACEPARENT: carrier["traceparent"],
|
||||
OTEL_RESOURCE_ATTRIBUTES: `${
|
||||
SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
}=trigger,${Object.entries(taskContext.attributes)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",")}`,
|
||||
},
|
||||
},
|
||||
throwOnError: false,
|
||||
@@ -223,7 +266,7 @@ export const python = {
|
||||
pythonBin,
|
||||
scriptPath,
|
||||
args: scriptArgs.join(" "),
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "brand-python",
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "python",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -243,6 +286,8 @@ export const python = {
|
||||
|
||||
const pythonScriptPath = createTempFileSync(`script_${Date.now()}.py`, scriptContent);
|
||||
|
||||
const carrier = carrierFromContext();
|
||||
|
||||
const pythonProcess = x(pythonBin, [pythonScriptPath], {
|
||||
...options,
|
||||
nodeOptions: {
|
||||
@@ -250,6 +295,12 @@ export const python = {
|
||||
env: {
|
||||
...process.env,
|
||||
...options.env,
|
||||
TRACEPARENT: carrier["traceparent"],
|
||||
OTEL_RESOURCE_ATTRIBUTES: `${
|
||||
SemanticInternalAttributes.EXECUTION_ENVIRONMENT
|
||||
}=trigger,${Object.entries(taskContext.attributes)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",")}`,
|
||||
},
|
||||
},
|
||||
throwOnError: false,
|
||||
@@ -260,7 +311,7 @@ export const python = {
|
||||
pythonBin,
|
||||
contentPreview:
|
||||
scriptContent.substring(0, 100) + (scriptContent.length > 100 ? "..." : ""),
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "brand-python",
|
||||
[SemanticInternalAttributes.STYLE_ICON]: "python",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1718,11 +1718,27 @@ importers:
|
||||
specifier: 3.23.8
|
||||
version: 3.23.8
|
||||
|
||||
references/agent-loop:
|
||||
references/bun-catalog:
|
||||
dependencies:
|
||||
'@trigger.dev/sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/trigger-sdk
|
||||
devDependencies:
|
||||
'@types/bun':
|
||||
specifier: ^1.1.6
|
||||
version: 1.1.6
|
||||
trigger.dev:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/cli-v3
|
||||
|
||||
references/d3-demo:
|
||||
dependencies:
|
||||
'@ai-sdk/openai':
|
||||
specifier: 1.3.3
|
||||
version: 1.3.3(zod@3.23.8)
|
||||
'@trigger.dev/python':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/python
|
||||
'@trigger.dev/react-hooks':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/react-hooks
|
||||
@@ -1759,10 +1775,16 @@ importers:
|
||||
zod:
|
||||
specifier: 3.23.8
|
||||
version: 3.23.8
|
||||
zod-to-json-schema:
|
||||
specifier: ^3.24.5
|
||||
version: 3.24.5(zod@3.23.8)
|
||||
devDependencies:
|
||||
'@tailwindcss/postcss':
|
||||
specifier: ^4
|
||||
version: 4.0.17
|
||||
'@trigger.dev/build':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/build
|
||||
'@types/node':
|
||||
specifier: ^20
|
||||
version: 20.14.14
|
||||
@@ -1782,19 +1804,6 @@ importers:
|
||||
specifier: ^5
|
||||
version: 5.5.4
|
||||
|
||||
references/bun-catalog:
|
||||
dependencies:
|
||||
'@trigger.dev/sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/trigger-sdk
|
||||
devDependencies:
|
||||
'@types/bun':
|
||||
specifier: ^1.1.6
|
||||
version: 1.1.6
|
||||
trigger.dev:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/cli-v3
|
||||
|
||||
references/hello-world:
|
||||
dependencies:
|
||||
'@trigger.dev/sdk':
|
||||
@@ -2251,7 +2260,7 @@ packages:
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.0
|
||||
eventsource-parser: 3.0.0
|
||||
nanoid: 3.3.7
|
||||
nanoid: 3.3.11
|
||||
secure-json-parse: 2.7.0
|
||||
zod: 3.23.8
|
||||
dev: true
|
||||
@@ -2487,7 +2496,7 @@ packages:
|
||||
json-schema: 0.4.0
|
||||
secure-json-parse: 2.7.0
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.23.5(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
dev: true
|
||||
|
||||
/@ai-sdk/ui-utils@1.0.0(zod@3.23.8):
|
||||
@@ -2502,7 +2511,7 @@ packages:
|
||||
'@ai-sdk/provider': 1.0.0
|
||||
'@ai-sdk/provider-utils': 2.0.0(zod@3.23.8)
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.23.5(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
dev: false
|
||||
|
||||
/@ai-sdk/ui-utils@1.0.1(zod@3.23.8):
|
||||
@@ -2517,7 +2526,7 @@ packages:
|
||||
'@ai-sdk/provider': 1.0.0
|
||||
'@ai-sdk/provider-utils': 2.0.1(zod@3.23.8)
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.23.5(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
dev: true
|
||||
|
||||
/@ai-sdk/ui-utils@1.2.1(zod@3.23.8):
|
||||
@@ -2529,7 +2538,7 @@ packages:
|
||||
'@ai-sdk/provider': 1.1.0
|
||||
'@ai-sdk/provider-utils': 2.2.1(zod@3.23.8)
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.5(zod@3.23.8)
|
||||
dev: false
|
||||
|
||||
/@ai-sdk/vue@0.0.45(vue@3.4.38)(zod@3.23.8):
|
||||
@@ -8413,20 +8422,20 @@ packages:
|
||||
/@internationalized/date@3.5.5:
|
||||
resolution: {integrity: sha512-H+CfYvOZ0LTJeeLOqm19E3uj/4YjrmOFtBufDHPfvtI80hFAMqtrp7oCACpe4Cil5l8S0Qu/9dYfZc/5lY8WQQ==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
dev: false
|
||||
|
||||
/@internationalized/message@3.1.1:
|
||||
resolution: {integrity: sha512-ZgHxf5HAPIaR0th+w0RUD62yF6vxitjlprSxmLJ1tam7FOekqRSDELMg4Cr/DdszG5YLsp5BG3FgHgqquQZbqw==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
intl-messageformat: 10.5.8
|
||||
dev: false
|
||||
|
||||
/@internationalized/message@3.1.4:
|
||||
resolution: {integrity: sha512-Dygi9hH1s7V9nha07pggCkvmRfDd3q2lWnMGvrJyrOwYMe1yj4D2T9BoH9I6MGR7xz0biQrtLPsqUkqXzIrBOw==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
intl-messageformat: 10.5.8
|
||||
dev: false
|
||||
|
||||
@@ -8439,7 +8448,7 @@ packages:
|
||||
/@internationalized/number@3.5.3:
|
||||
resolution: {integrity: sha512-rd1wA3ebzlp0Mehj5YTuTI50AQEx80gWFyHcQu+u91/5NgdwBecO8BH6ipPfE+lmQ9d63vpB3H9SHoIUiupllw==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
dev: false
|
||||
|
||||
/@internationalized/string@3.2.0:
|
||||
@@ -8451,7 +8460,7 @@ packages:
|
||||
/@internationalized/string@3.2.3:
|
||||
resolution: {integrity: sha512-9kpfLoA8HegiWTeCbR2livhdVeKobCnVv8tlJ6M2jF+4tcMqDo94ezwlnrUANBWPgd8U7OXIHCk2Ov2qhk4KXw==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
dev: false
|
||||
|
||||
/@ioredis/commands@1.2.0:
|
||||
@@ -12541,7 +12550,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/breadcrumbs': 3.7.7(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -12585,7 +12594,7 @@ packages:
|
||||
'@react-stately/toggle': 3.7.7(react@18.2.0)
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -12604,7 +12613,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/calendar': 3.4.9(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12662,7 +12671,7 @@ packages:
|
||||
'@react-stately/toggle': 3.7.7(react@18.2.0)
|
||||
'@react-types/checkbox': 3.8.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -12686,7 +12695,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/combobox': 3.12.1(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12739,7 +12748,7 @@ packages:
|
||||
'@react-types/datepicker': 3.8.2(react@18.2.0)
|
||||
'@react-types/dialog': 3.5.12(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12799,7 +12808,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/dialog': 3.5.12(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12839,7 +12848,7 @@ packages:
|
||||
'@react-stately/dnd': 3.4.2(react@18.2.0)
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12865,7 +12874,7 @@ packages:
|
||||
'@react-aria/interactions': 3.22.2(react@18.2.0)
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
clsx: 2.1.1
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
@@ -12892,7 +12901,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-stately/form': 3.0.5(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -12914,7 +12923,7 @@ packages:
|
||||
'@react-types/checkbox': 3.8.3(react@18.2.0)
|
||||
'@react-types/grid': 3.2.8(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12938,7 +12947,7 @@ packages:
|
||||
'@react-types/checkbox': 3.6.0(react@18.2.0)
|
||||
'@react-types/grid': 3.2.3(react@18.2.0)
|
||||
'@react-types/shared': 3.22.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -12978,7 +12987,7 @@ packages:
|
||||
'@react-stately/list': 3.10.8(react@18.2.0)
|
||||
'@react-stately/tree': 3.8.4(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13011,7 +13020,7 @@ packages:
|
||||
'@react-aria/ssr': 3.9.5(react@18.2.0)
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13035,7 +13044,7 @@ packages:
|
||||
'@react-aria/ssr': 3.9.5(react@18.2.0)
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13046,7 +13055,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13085,7 +13094,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/link': 3.5.7(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13122,7 +13131,7 @@ packages:
|
||||
'@react-stately/list': 3.10.8(react@18.2.0)
|
||||
'@react-types/listbox': 3.5.1(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13130,13 +13139,13 @@ packages:
|
||||
/@react-aria/live-announcer@3.3.1:
|
||||
resolution: {integrity: sha512-hsc77U7S16trM86d+peqJCOCQ7/smO1cybgdpOuzXyiwcHQw8RQ4GrXrS37P4Ux/44E9nMZkOwATQRT2aK8+Ew==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
dev: false
|
||||
|
||||
/@react-aria/live-announcer@3.3.4:
|
||||
resolution: {integrity: sha512-w8lxs35QrRrn6pBNzVfyGOeqWdxeVKf9U6bXIVwhq7rrTqRULL8jqy8RJIMfIs1s8G5FpwWYjyBOjl2g5Cu1iA==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
dev: false
|
||||
|
||||
/@react-aria/menu@3.12.0(react-dom@18.2.0)(react@18.2.0):
|
||||
@@ -13180,7 +13189,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/menu': 3.9.11(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13193,7 +13202,7 @@ packages:
|
||||
'@react-aria/progress': 3.4.16(react@18.2.0)
|
||||
'@react-types/meter': 3.4.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13246,7 +13255,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/numberfield': 3.8.5(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13288,7 +13297,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/overlays': 3.8.9(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13303,7 +13312,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/progress': 3.5.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13353,7 +13362,7 @@ packages:
|
||||
'@react-stately/radio': 3.10.7(react@18.2.0)
|
||||
'@react-types/radio': 3.8.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13385,7 +13394,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/searchfield': 3.5.8(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13432,7 +13441,7 @@ packages:
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/select': 3.9.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13466,7 +13475,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-stately/selection': 3.16.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13489,7 +13498,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13506,7 +13515,7 @@ packages:
|
||||
'@react-stately/slider': 3.5.7(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/slider': 3.7.5(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13554,7 +13563,7 @@ packages:
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13575,7 +13584,7 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13600,7 +13609,7 @@ packages:
|
||||
'@react-stately/toggle': 3.7.7(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/switch': 3.5.5(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13650,7 +13659,7 @@ packages:
|
||||
'@react-types/grid': 3.2.8(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/table': 3.10.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13686,7 +13695,7 @@ packages:
|
||||
'@react-stately/tabs': 3.6.9(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/tabs': 3.3.9(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13726,7 +13735,7 @@ packages:
|
||||
'@react-stately/list': 3.10.8(react@18.2.0)
|
||||
'@react-types/button': 3.9.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -13761,7 +13770,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/textfield': 3.9.6(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13775,7 +13784,7 @@ packages:
|
||||
'@react-aria/utils': 3.23.0(react@18.2.0)
|
||||
'@react-stately/toggle': 3.7.0(react@18.2.0)
|
||||
'@react-types/checkbox': 3.6.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13790,7 +13799,7 @@ packages:
|
||||
'@react-stately/toggle': 3.7.7(react@18.2.0)
|
||||
'@react-types/checkbox': 3.8.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13820,7 +13829,7 @@ packages:
|
||||
'@react-stately/tooltip': 3.4.12(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/tooltip': 3.4.11(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -13845,7 +13854,7 @@ packages:
|
||||
'@react-aria/ssr': 3.9.5(react@18.2.0)
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
clsx: 2.1.1
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
@@ -13858,7 +13867,7 @@ packages:
|
||||
'@react-aria/interactions': 3.22.2(react@18.2.0)
|
||||
'@react-aria/utils': 3.25.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14546,7 +14555,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/calendar': 3.4.9(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14572,7 +14581,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/checkbox': 3.8.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14592,7 +14601,7 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
dependencies:
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14626,7 +14635,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/combobox': 3.12.1(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14652,7 +14661,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/datepicker': 3.8.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14690,7 +14699,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-stately/selection': 3.16.2(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14703,7 +14712,7 @@ packages:
|
||||
/@react-stately/flags@3.0.3:
|
||||
resolution: {integrity: sha512-/ha7XFA0RZTQsbzSPwu3KkbNMgbvuM0GuMTYLTBWpgBrovBNTM+QqI/PfZTdHg8PwCYF4H5Y8gjdSpdulCvJFw==}
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
dev: false
|
||||
|
||||
/@react-stately/form@3.0.0(react@18.2.0):
|
||||
@@ -14722,7 +14731,7 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
dependencies:
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14735,7 +14744,7 @@ packages:
|
||||
'@react-stately/selection': 3.14.2(react@18.2.0)
|
||||
'@react-types/grid': 3.2.3(react@18.2.0)
|
||||
'@react-types/shared': 3.22.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14748,7 +14757,7 @@ packages:
|
||||
'@react-stately/selection': 3.16.2(react@18.2.0)
|
||||
'@react-types/grid': 3.2.8(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14774,7 +14783,7 @@ packages:
|
||||
'@react-stately/selection': 3.16.2(react@18.2.0)
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14798,7 +14807,7 @@ packages:
|
||||
'@react-stately/overlays': 3.6.10(react@18.2.0)
|
||||
'@react-types/menu': 3.9.11(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14824,7 +14833,7 @@ packages:
|
||||
'@react-stately/form': 3.0.5(react@18.2.0)
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/numberfield': 3.8.5(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14835,7 +14844,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/overlays': 3.8.9(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14872,7 +14881,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/radio': 3.8.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14894,7 +14903,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/searchfield': 3.5.8(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14922,7 +14931,7 @@ packages:
|
||||
'@react-stately/overlays': 3.6.10(react@18.2.0)
|
||||
'@react-types/select': 3.9.6(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14946,7 +14955,7 @@ packages:
|
||||
'@react-stately/collections': 3.10.9(react@18.2.0)
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -14970,7 +14979,7 @@ packages:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/slider': 3.7.5(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15004,7 +15013,7 @@ packages:
|
||||
'@react-types/grid': 3.2.8(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/table': 3.10.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15028,7 +15037,7 @@ packages:
|
||||
'@react-stately/list': 3.10.8(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@react-types/tabs': 3.3.9(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15050,7 +15059,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/checkbox': 3.8.3(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15061,7 +15070,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-stately/overlays': 3.6.10(react@18.2.0)
|
||||
'@react-types/tooltip': 3.4.11(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15098,7 +15107,7 @@ packages:
|
||||
'@react-stately/selection': 3.16.2(react@18.2.0)
|
||||
'@react-stately/utils': 3.10.3(react@18.2.0)
|
||||
'@react-types/shared': 3.24.1(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15107,7 +15116,7 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -15127,7 +15136,7 @@ packages:
|
||||
dependencies:
|
||||
'@react-aria/utils': 3.23.0(react@18.2.0)
|
||||
'@react-types/shared': 3.22.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.5
|
||||
'@swc/helpers': 0.5.15
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -17379,7 +17388,7 @@ packages:
|
||||
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
dev: false
|
||||
|
||||
/@swc/types@0.1.6:
|
||||
@@ -19442,7 +19451,7 @@ packages:
|
||||
secure-json-parse: 2.7.0
|
||||
svelte: 4.2.19
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.23.5(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
transitivePeerDependencies:
|
||||
- solid-js
|
||||
- vue
|
||||
@@ -19468,7 +19477,7 @@ packages:
|
||||
jsondiffpatch: 0.6.0
|
||||
react: 18.3.1
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.23.5(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
dev: false
|
||||
|
||||
/ai@4.0.2(react@18.3.1)(zod@3.23.8):
|
||||
@@ -19491,7 +19500,7 @@ packages:
|
||||
jsondiffpatch: 0.6.0
|
||||
react: 18.3.1
|
||||
zod: 3.23.8
|
||||
zod-to-json-schema: 3.23.5(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.3(zod@3.23.8)
|
||||
dev: true
|
||||
|
||||
/ai@4.2.5(react@19.0.0)(zod@3.23.8):
|
||||
@@ -21007,6 +21016,7 @@ packages:
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
requiresBuild: true
|
||||
|
||||
/color-string@1.9.1:
|
||||
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
|
||||
@@ -22265,6 +22275,7 @@ packages:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.2.1
|
||||
dev: true
|
||||
|
||||
/enhanced-resolve@5.18.1:
|
||||
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
|
||||
@@ -22272,7 +22283,6 @@ packages:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.2.1
|
||||
dev: true
|
||||
|
||||
/enquirer@2.3.6:
|
||||
resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
|
||||
@@ -23926,7 +23936,7 @@ packages:
|
||||
resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==}
|
||||
engines: {node: '>= 12'}
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
dev: false
|
||||
|
||||
/fill-range@7.0.1:
|
||||
@@ -24167,7 +24177,7 @@ packages:
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@emotion/is-prop-valid': 0.8.8
|
||||
dev: false
|
||||
@@ -24283,7 +24293,7 @@ packages:
|
||||
resolution: {integrity: sha512-igqQZTtS4GFwkDWA5gFWQye9Lmkx184Y17+x9flFq8HC68RVuuQGPeQtBFdMMlnac7/2Bq1n+1rkp4S8ZAu7kA==}
|
||||
dependencies:
|
||||
adm-zip: 0.5.16
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
dev: false
|
||||
|
||||
/generic-names@4.0.0:
|
||||
@@ -25242,6 +25252,7 @@ packages:
|
||||
|
||||
/is-arrayish@0.3.2:
|
||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/is-bigint@1.0.4:
|
||||
@@ -27487,7 +27498,6 @@ packages:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/nanoid@3.3.4:
|
||||
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
||||
@@ -29235,7 +29245,7 @@ packages:
|
||||
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.0
|
||||
dev: false
|
||||
@@ -29252,7 +29262,7 @@ packages:
|
||||
resolution: {integrity: sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.0
|
||||
|
||||
@@ -31513,6 +31523,7 @@ packages:
|
||||
|
||||
/simple-swizzle@0.2.2:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
is-arrayish: 0.3.2
|
||||
dev: false
|
||||
@@ -32399,7 +32410,7 @@ packages:
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@pkgr/utils': 2.3.1
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
dev: true
|
||||
|
||||
/tailwind-merge@1.12.0:
|
||||
@@ -34823,7 +34834,7 @@ packages:
|
||||
acorn-import-assertions: 1.9.0(acorn@8.12.1)
|
||||
browserslist: 4.23.3
|
||||
chrome-trace-event: 1.0.3
|
||||
enhanced-resolve: 5.15.0
|
||||
enhanced-resolve: 5.18.1
|
||||
es-module-lexer: 1.3.1
|
||||
eslint-scope: 5.1.1
|
||||
events: 3.3.0
|
||||
@@ -34863,7 +34874,7 @@ packages:
|
||||
acorn-import-assertions: 1.9.0(acorn@8.12.1)
|
||||
browserslist: 4.23.3
|
||||
chrome-trace-event: 1.0.3
|
||||
enhanced-resolve: 5.15.0
|
||||
enhanced-resolve: 5.18.1
|
||||
es-module-lexer: 1.3.1
|
||||
eslint-scope: 5.1.1
|
||||
events: 3.3.0
|
||||
@@ -35328,15 +35339,15 @@ packages:
|
||||
zod: 3.23.8
|
||||
dev: false
|
||||
|
||||
/zod-to-json-schema@3.23.5(zod@3.23.8):
|
||||
resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==}
|
||||
/zod-to-json-schema@3.24.3(zod@3.23.8):
|
||||
resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==}
|
||||
peerDependencies:
|
||||
zod: ^3.23.3
|
||||
zod: ^3.24.1
|
||||
dependencies:
|
||||
zod: 3.23.8
|
||||
|
||||
/zod-to-json-schema@3.24.3(zod@3.23.8):
|
||||
resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==}
|
||||
/zod-to-json-schema@3.24.5(zod@3.23.8):
|
||||
resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==}
|
||||
peerDependencies:
|
||||
zod: ^3.24.1
|
||||
dependencies:
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const AgentLoopMetadata = z.object({
|
||||
waitToken: z.object({
|
||||
id: z.string(),
|
||||
publicAccessToken: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type AgentLoopMetadata = z.infer<typeof AgentLoopMetadata>;
|
||||
@@ -1,7 +0,0 @@
|
||||
import { defineConfig } from "@trigger.dev/sdk";
|
||||
|
||||
export default defineConfig({
|
||||
project: "proj_hgknlxlflfcemqogujnt",
|
||||
dirs: ["./src/trigger"],
|
||||
maxDuration: 3600,
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "references-agent-loop",
|
||||
"name": "references-d3-demo",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -8,10 +8,18 @@
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"dev:trigger": "trigger dev",
|
||||
"deploy": "trigger deploy"
|
||||
"deploy": "pnpm run python:compile-requirements && trigger deploy",
|
||||
"python:install-requirements": "uv pip sync requirements.txt",
|
||||
"python:compile-requirements": "uv pip compile requirements.in -o requirements.txt",
|
||||
"python:install": "pnpm run python:compile-requirements && pnpm run python:install-requirements",
|
||||
"python:create-env": "uv venv .venv"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "1.3.3",
|
||||
"@trigger.dev/react-hooks": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"@trigger.dev/python": "workspace:*",
|
||||
"ai": "4.2.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.484.0",
|
||||
@@ -20,10 +28,8 @@
|
||||
"react-dom": "^19.0.0",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tw-animate-css": "^1.2.4",
|
||||
"@trigger.dev/react-hooks": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"ai": "4.2.5",
|
||||
"zod": "3.23.8"
|
||||
"zod": "3.23.8",
|
||||
"zod-to-json-schema": "^3.24.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
@@ -31,7 +37,8 @@
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"tailwindcss": "^4.0.17",
|
||||
"typescript": "^5",
|
||||
"trigger.dev": "workspace:*"
|
||||
"trigger.dev": "workspace:*",
|
||||
"@trigger.dev/build": "workspace:*",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 128 B |
|
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
@@ -0,0 +1,5 @@
|
||||
crawl4ai
|
||||
playwright
|
||||
openai-agents
|
||||
urllib3<2.0.0
|
||||
logfire
|
||||
@@ -0,0 +1,309 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile requirements.in -o requirements.txt
|
||||
aiofiles==24.1.0
|
||||
# via crawl4ai
|
||||
aiohappyeyeballs==2.6.1
|
||||
# via aiohttp
|
||||
aiohttp==3.11.14
|
||||
# via
|
||||
# crawl4ai
|
||||
# litellm
|
||||
aiosignal==1.3.2
|
||||
# via aiohttp
|
||||
aiosqlite==0.21.0
|
||||
# via crawl4ai
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==4.9.0
|
||||
# via
|
||||
# httpx
|
||||
# mcp
|
||||
# openai
|
||||
# sse-starlette
|
||||
# starlette
|
||||
attrs==25.3.0
|
||||
# via
|
||||
# aiohttp
|
||||
# jsonschema
|
||||
# referencing
|
||||
beautifulsoup4==4.13.3
|
||||
# via crawl4ai
|
||||
certifi==2025.1.31
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
# requests
|
||||
cffi==1.17.1
|
||||
# via cryptography
|
||||
charset-normalizer==3.4.1
|
||||
# via requests
|
||||
click==8.1.8
|
||||
# via
|
||||
# crawl4ai
|
||||
# litellm
|
||||
# nltk
|
||||
# uvicorn
|
||||
colorama==0.4.6
|
||||
# via
|
||||
# crawl4ai
|
||||
# griffe
|
||||
crawl4ai==0.5.0.post8
|
||||
# via -r requirements.in
|
||||
cryptography==44.0.2
|
||||
# via pyopenssl
|
||||
cssselect==1.3.0
|
||||
# via crawl4ai
|
||||
deprecated==1.2.18
|
||||
# via
|
||||
# opentelemetry-api
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# opentelemetry-semantic-conventions
|
||||
distro==1.9.0
|
||||
# via openai
|
||||
executing==2.2.0
|
||||
# via logfire
|
||||
fake-http-header==0.3.5
|
||||
# via tf-playwright-stealth
|
||||
fake-useragent==2.1.0
|
||||
# via crawl4ai
|
||||
faust-cchardet==2.1.19
|
||||
# via crawl4ai
|
||||
filelock==3.18.0
|
||||
# via huggingface-hub
|
||||
frozenlist==1.5.0
|
||||
# via
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
fsspec==2025.3.0
|
||||
# via huggingface-hub
|
||||
googleapis-common-protos==1.69.2
|
||||
# via opentelemetry-exporter-otlp-proto-http
|
||||
greenlet==3.1.1
|
||||
# via playwright
|
||||
griffe==1.6.3
|
||||
# via openai-agents
|
||||
h11==0.14.0
|
||||
# via
|
||||
# httpcore
|
||||
# uvicorn
|
||||
httpcore==1.0.7
|
||||
# via httpx
|
||||
httpx==0.28.1
|
||||
# via
|
||||
# crawl4ai
|
||||
# litellm
|
||||
# mcp
|
||||
# openai
|
||||
httpx-sse==0.4.0
|
||||
# via mcp
|
||||
huggingface-hub==0.29.3
|
||||
# via tokenizers
|
||||
humanize==4.12.2
|
||||
# via crawl4ai
|
||||
idna==3.10
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
# requests
|
||||
# yarl
|
||||
importlib-metadata==8.6.1
|
||||
# via
|
||||
# litellm
|
||||
# opentelemetry-api
|
||||
jinja2==3.1.6
|
||||
# via litellm
|
||||
jiter==0.9.0
|
||||
# via openai
|
||||
joblib==1.4.2
|
||||
# via nltk
|
||||
jsonschema==4.23.0
|
||||
# via litellm
|
||||
jsonschema-specifications==2024.10.1
|
||||
# via jsonschema
|
||||
litellm==1.64.1
|
||||
# via crawl4ai
|
||||
logfire==3.11.0
|
||||
# via -r requirements.in
|
||||
lxml==5.3.1
|
||||
# via crawl4ai
|
||||
markdown-it-py==3.0.0
|
||||
# via rich
|
||||
markupsafe==3.0.2
|
||||
# via jinja2
|
||||
mcp==1.5.0
|
||||
# via openai-agents
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
multidict==6.2.0
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
nltk==3.9.1
|
||||
# via crawl4ai
|
||||
numpy==2.2.4
|
||||
# via
|
||||
# crawl4ai
|
||||
# rank-bm25
|
||||
openai==1.68.2
|
||||
# via
|
||||
# litellm
|
||||
# openai-agents
|
||||
openai-agents==0.0.7
|
||||
# via -r requirements.in
|
||||
opentelemetry-api==1.31.1
|
||||
# via
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# opentelemetry-instrumentation
|
||||
# opentelemetry-sdk
|
||||
# opentelemetry-semantic-conventions
|
||||
opentelemetry-exporter-otlp-proto-common==1.31.1
|
||||
# via opentelemetry-exporter-otlp-proto-http
|
||||
opentelemetry-exporter-otlp-proto-http==1.31.1
|
||||
# via logfire
|
||||
opentelemetry-instrumentation==0.52b1
|
||||
# via logfire
|
||||
opentelemetry-proto==1.31.1
|
||||
# via
|
||||
# opentelemetry-exporter-otlp-proto-common
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
opentelemetry-sdk==1.31.1
|
||||
# via
|
||||
# logfire
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
opentelemetry-semantic-conventions==0.52b1
|
||||
# via
|
||||
# opentelemetry-instrumentation
|
||||
# opentelemetry-sdk
|
||||
packaging==24.2
|
||||
# via
|
||||
# huggingface-hub
|
||||
# opentelemetry-instrumentation
|
||||
pillow==10.4.0
|
||||
# via crawl4ai
|
||||
playwright==1.51.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# crawl4ai
|
||||
# tf-playwright-stealth
|
||||
propcache==0.3.1
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
protobuf==5.29.4
|
||||
# via
|
||||
# googleapis-common-protos
|
||||
# logfire
|
||||
# opentelemetry-proto
|
||||
psutil==7.0.0
|
||||
# via crawl4ai
|
||||
pycparser==2.22
|
||||
# via cffi
|
||||
pydantic==2.10.6
|
||||
# via
|
||||
# crawl4ai
|
||||
# litellm
|
||||
# mcp
|
||||
# openai
|
||||
# openai-agents
|
||||
# pydantic-settings
|
||||
pydantic-core==2.27.2
|
||||
# via pydantic
|
||||
pydantic-settings==2.8.1
|
||||
# via mcp
|
||||
pyee==12.1.1
|
||||
# via playwright
|
||||
pygments==2.19.1
|
||||
# via rich
|
||||
pyopenssl==25.0.0
|
||||
# via crawl4ai
|
||||
pyperclip==1.9.0
|
||||
# via crawl4ai
|
||||
python-dotenv==1.1.0
|
||||
# via
|
||||
# crawl4ai
|
||||
# litellm
|
||||
# pydantic-settings
|
||||
pyyaml==6.0.2
|
||||
# via huggingface-hub
|
||||
rank-bm25==0.2.2
|
||||
# via crawl4ai
|
||||
referencing==0.36.2
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
regex==2024.11.6
|
||||
# via
|
||||
# nltk
|
||||
# tiktoken
|
||||
requests==2.32.3
|
||||
# via
|
||||
# crawl4ai
|
||||
# huggingface-hub
|
||||
# openai-agents
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# tiktoken
|
||||
rich==13.9.4
|
||||
# via
|
||||
# crawl4ai
|
||||
# logfire
|
||||
rpds-py==0.24.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
sniffio==1.3.1
|
||||
# via
|
||||
# anyio
|
||||
# openai
|
||||
snowballstemmer==2.2.0
|
||||
# via crawl4ai
|
||||
soupsieve==2.6
|
||||
# via beautifulsoup4
|
||||
sse-starlette==2.2.1
|
||||
# via mcp
|
||||
starlette==0.46.1
|
||||
# via
|
||||
# mcp
|
||||
# sse-starlette
|
||||
tf-playwright-stealth==1.1.2
|
||||
# via crawl4ai
|
||||
tiktoken==0.9.0
|
||||
# via litellm
|
||||
tokenizers==0.21.1
|
||||
# via litellm
|
||||
tqdm==4.67.1
|
||||
# via
|
||||
# huggingface-hub
|
||||
# nltk
|
||||
# openai
|
||||
types-requests==2.31.0.6
|
||||
# via openai-agents
|
||||
types-urllib3==1.26.25.14
|
||||
# via types-requests
|
||||
typing-extensions==4.13.0
|
||||
# via
|
||||
# aiosqlite
|
||||
# beautifulsoup4
|
||||
# huggingface-hub
|
||||
# logfire
|
||||
# openai
|
||||
# openai-agents
|
||||
# opentelemetry-sdk
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# pyee
|
||||
urllib3==1.26.20
|
||||
# via
|
||||
# -r requirements.in
|
||||
# requests
|
||||
uvicorn==0.34.0
|
||||
# via mcp
|
||||
wrapt==1.17.2
|
||||
# via
|
||||
# deprecated
|
||||
# opentelemetry-instrumentation
|
||||
xxhash==3.5.0
|
||||
# via crawl4ai
|
||||
yarl==1.18.3
|
||||
# via aiohttp
|
||||
zipp==3.21.0
|
||||
# via importlib-metadata
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
@@ -2,7 +2,7 @@ import MainApp from "@/components/main-app";
|
||||
import { auth } from "@trigger.dev/sdk";
|
||||
|
||||
export default async function Home() {
|
||||
const publicAccessToken = await auth.createTriggerPublicToken("agent-loop-example");
|
||||
const publicAccessToken = await auth.createTriggerPublicToken("chat-example");
|
||||
|
||||
return <MainApp publicAccessToken={publicAccessToken} />;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { Send } from "lucide-react";
|
||||
import ChatInterface from "@/components/chat-interface";
|
||||
import InitialPrompt from "@/components/initial-prompt";
|
||||
import { useRealtimeTaskTriggerWithStreams, useWaitToken } from "@trigger.dev/react-hooks";
|
||||
import type { agentLoopExample } from "@/trigger/agent";
|
||||
import type { chatExample } from "@/trigger/chat";
|
||||
import { AgentLoopMetadata } from "@/trigger/schemas";
|
||||
import type { TextStreamPart } from "ai";
|
||||
|
||||
@@ -17,14 +17,14 @@ type ResponseStreams = {
|
||||
[K in `responses.${number | string}`]: TextStreamPart<{}>;
|
||||
};
|
||||
|
||||
export function useAgentLoop({ publicAccessToken }: { publicAccessToken: string }) {
|
||||
const triggerInstance = useRealtimeTaskTriggerWithStreams<
|
||||
typeof agentLoopExample,
|
||||
ResponseStreams
|
||||
>("agent-loop-example", {
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: process.env.NEXT_PUBLIC_TRIGGER_API_URL,
|
||||
});
|
||||
export function useChat({ publicAccessToken }: { publicAccessToken: string }) {
|
||||
const triggerInstance = useRealtimeTaskTriggerWithStreams<typeof chatExample, ResponseStreams>(
|
||||
"chat-example",
|
||||
{
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: process.env.NEXT_PUBLIC_TRIGGER_API_URL,
|
||||
}
|
||||
);
|
||||
const [conversation, setConversation] = useState<ChatConversation>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [waitToken, setWaitToken] = useState<AgentLoopMetadata | null>(null);
|
||||
@@ -86,7 +86,7 @@ export function useAgentLoop({ publicAccessToken }: { publicAccessToken: string
|
||||
}
|
||||
|
||||
export default function MainApp({ publicAccessToken }: { publicAccessToken: string }) {
|
||||
const { continueConversation, conversation, isLoading } = useAgentLoop({ publicAccessToken });
|
||||
const { continueConversation, conversation, isLoading } = useChat({ publicAccessToken });
|
||||
const [input, setInput] = useState("");
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { BuildContext, BuildExtension } from "@trigger.dev/build";
|
||||
|
||||
// This is a custom build extension to install Playwright and Chromium
|
||||
export function installPlaywrightChromium(): BuildExtension {
|
||||
return {
|
||||
name: "InstallPlaywrightChromium",
|
||||
onBuildComplete(context: BuildContext) {
|
||||
const instructions = [
|
||||
// Base and Chromium dependencies
|
||||
`RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl unzip npm libnspr4 libatk1.0-0 libatk-bridge2.0-0 libatspi2.0-0 \
|
||||
libasound2 libnss3 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
|
||||
libgbm1 libxkbcommon0 \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*`,
|
||||
|
||||
// Install Playwright and Chromium
|
||||
`RUN npm install -g playwright`,
|
||||
`RUN mkdir -p /ms-playwright`,
|
||||
`RUN PLAYWRIGHT_BROWSERS_PATH=/ms-playwright python -m playwright install --with-deps chromium`,
|
||||
];
|
||||
|
||||
context.addLayer({
|
||||
id: "playwright",
|
||||
image: { instructions },
|
||||
deploy: {
|
||||
env: {
|
||||
PLAYWRIGHT_BROWSERS_PATH: "/ms-playwright",
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1",
|
||||
PLAYWRIGHT_SKIP_BROWSER_VALIDATION: "1",
|
||||
},
|
||||
override: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import { z } from "zod";
|
||||
|
||||
const MAX_STEPS = 10;
|
||||
|
||||
export const agentLoopExample = schemaTask({
|
||||
id: "agent-loop-example",
|
||||
description: "Agent loop example",
|
||||
export const chatExample = schemaTask({
|
||||
id: "chat-example",
|
||||
description: "Chat example",
|
||||
schema: z.object({
|
||||
model: z.string().default("chatgpt-4o-latest"),
|
||||
prompt: z.string().default("Hello, how are you?"),
|
||||
@@ -0,0 +1,197 @@
|
||||
import { batch, logger, schemaTask, wait } from "@trigger.dev/sdk/v3";
|
||||
import { createHash } from "node:crypto";
|
||||
import { z } from "zod";
|
||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
||||
import { CSVRowPayload, RowEnrichmentResult } from "./schemas";
|
||||
import { python } from "@trigger.dev/python";
|
||||
|
||||
export const d3Demo = schemaTask({
|
||||
id: "d3-demo",
|
||||
description: "D3 Demo - Enriches a CSV dataset using Trigger.dev and the python OpenAI Agent SDK",
|
||||
schema: z.object({
|
||||
url: z.string().url().describe("The URL of the CSV dataset to enrich"),
|
||||
}),
|
||||
run: async ({ url }) => {
|
||||
const response = await fetch(url);
|
||||
const csv = await response.text();
|
||||
|
||||
const header = csv.split("\n")[0];
|
||||
const rows = csv.split("\n").slice(1);
|
||||
|
||||
// Each payload has a header, url, and a single row
|
||||
const payloads = rows.map((row) => ({
|
||||
header,
|
||||
url,
|
||||
row,
|
||||
}));
|
||||
|
||||
const chunkedPayloads = [];
|
||||
|
||||
for (let i = 0; i < payloads.length; i += 500) {
|
||||
chunkedPayloads.push(payloads.slice(i, i + 500));
|
||||
}
|
||||
|
||||
// Process each chunk
|
||||
for (const chunk of chunkedPayloads) {
|
||||
const results = await batch.triggerAndWait<typeof handleCSVRow>(
|
||||
chunk.map((payload) => ({
|
||||
id: "handle-csv-row",
|
||||
payload,
|
||||
options: {
|
||||
idempotencyKey: createIdempotencyKey(payload),
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
for (const result of results.runs) {
|
||||
if (result.ok) {
|
||||
// Pretty print the result of the enrichment
|
||||
logger.log(`Enriched row ${result.output.row}`, result.output);
|
||||
} else {
|
||||
logger.error(`Error enriching row: ${result.error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const ROW_AGENT_RUNNER_COUNT = 10;
|
||||
|
||||
export const handleCSVRow = schemaTask({
|
||||
id: "handle-csv-row",
|
||||
description: "Handles a single row of a CSV dataset",
|
||||
schema: CSVRowPayload,
|
||||
run: async ({ header, row, url }) => {
|
||||
// Batch trigger the rowAgentCoordinator to try 10 different enrichments on the same row
|
||||
const results = await batch.triggerAndWait<typeof rowAgentCoordinator>(
|
||||
Array.from({ length: ROW_AGENT_RUNNER_COUNT }, () => ({
|
||||
id: "row-agent-coordinator" as const,
|
||||
payload: { row: { header, row, url } },
|
||||
}))
|
||||
);
|
||||
|
||||
const outputs = results.runs.filter((result) => result.ok).map((result) => result.output);
|
||||
|
||||
const enrichment = await enrichmentResultsEvaluator
|
||||
.triggerAndWait({
|
||||
results: outputs,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
return {
|
||||
enrichment,
|
||||
header,
|
||||
row,
|
||||
url,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const rowAgentCoordinator = schemaTask({
|
||||
id: "row-agent-coordinator",
|
||||
description: "Coordinators the row agent",
|
||||
schema: z.object({
|
||||
row: CSVRowPayload,
|
||||
}),
|
||||
run: async ({ row }) => {
|
||||
const waitToken = await wait.createToken({
|
||||
timeout: "2m",
|
||||
});
|
||||
|
||||
const jsonSchema = zodToJsonSchema(RowEnrichmentResult);
|
||||
|
||||
await rowAgentRunner.trigger({
|
||||
row,
|
||||
waitToken,
|
||||
jsonSchema,
|
||||
});
|
||||
|
||||
const result = await wait.forToken<RowEnrichmentResult>(waitToken.id);
|
||||
|
||||
if (!result.ok) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return result.output;
|
||||
},
|
||||
});
|
||||
|
||||
export const rowAgentRunner = schemaTask({
|
||||
id: "row-agent-runner",
|
||||
description: "Runs the row agent",
|
||||
schema: z.object({
|
||||
row: CSVRowPayload,
|
||||
waitToken: z.object({
|
||||
id: z
|
||||
.string()
|
||||
.describe(
|
||||
"The wait token that the OpenAI Agent SDK will use to signal back to the coordinator"
|
||||
),
|
||||
publicAccessToken: z
|
||||
.string()
|
||||
.describe(
|
||||
"The public access token that the OpenAI Agent SDK will use to signal back to the coordinator"
|
||||
),
|
||||
}),
|
||||
jsonSchema: z.any().describe("The JSON schema of the result"),
|
||||
disableWaitTokenCompletion: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Whether to disable wait token completion"),
|
||||
}),
|
||||
run: async ({ row, waitToken, jsonSchema, disableWaitTokenCompletion }) => {
|
||||
const inputData = JSON.stringify({
|
||||
row,
|
||||
waitToken,
|
||||
jsonSchema,
|
||||
disableWaitTokenCompletion,
|
||||
});
|
||||
|
||||
logger.info("process.env", {
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
const result = await python.runScript("./src/trigger/python/agent.py", [inputData]);
|
||||
|
||||
logger.debug("row-agent-runner", {
|
||||
result,
|
||||
});
|
||||
|
||||
return {} as unknown as RowEnrichmentResult;
|
||||
},
|
||||
catchError: async ({ error }) => {
|
||||
logger.error("row-agent-runner", {
|
||||
error,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const enrichmentResultsEvaluator = schemaTask({
|
||||
id: "enrichment-results-evaluator",
|
||||
description: "Evaluates the enrichment results",
|
||||
schema: z.object({
|
||||
results: z.array(RowEnrichmentResult),
|
||||
}),
|
||||
run: async ({ results }) => {
|
||||
// Combine the results into a single object (this is a placeholder for a more complex evaluation)
|
||||
const combinedResult = results.reduce(
|
||||
(acc, result) => ({
|
||||
...acc,
|
||||
...result,
|
||||
}),
|
||||
{} as RowEnrichmentResult
|
||||
);
|
||||
|
||||
return combinedResult;
|
||||
},
|
||||
});
|
||||
|
||||
// Create a hash of the payload using Node.js crypto
|
||||
// Ideally, you'd do a stable serialization of the payload before hashing, to ensure the same payload always results in the same hash
|
||||
function createIdempotencyKey(payload: CSVRowPayload): string {
|
||||
const content = `${payload.header}\n${payload.row}`;
|
||||
|
||||
const hash = createHash("sha256");
|
||||
hash.update(content);
|
||||
return hash.digest("hex");
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from agents import Agent, Runner, WebSearchTool, trace
|
||||
import logfire
|
||||
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
||||
from logfire.propagate import attach_context
|
||||
|
||||
logfire.configure(
|
||||
service_name='d3-demo',
|
||||
send_to_logfire="if-token-present",
|
||||
distributed_tracing=True
|
||||
)
|
||||
logfire.instrument_openai_agents()
|
||||
|
||||
class CSVRowPayload(BaseModel):
|
||||
header: str = Field(description="The header of the CSV dataset")
|
||||
row: str = Field(description="The row to handle")
|
||||
url: str = Field(description="The URL of the CSV dataset")
|
||||
|
||||
class WaitToken(BaseModel):
|
||||
id: str = Field(description="The wait token ID")
|
||||
publicAccessToken: str = Field(description="The public access token for authorization")
|
||||
|
||||
class AgentInput(BaseModel):
|
||||
row: CSVRowPayload
|
||||
waitToken: WaitToken
|
||||
jsonSchema: dict
|
||||
disableWaitTokenCompletion: bool = Field(default=False, description="Whether to disable wait token completion")
|
||||
|
||||
class BasicInfo(BaseModel):
|
||||
name: str = Field(description="The name of the row")
|
||||
email: str = Field(description="The email of the row")
|
||||
firstName: Optional[str] = Field(None, description="The first name of the row")
|
||||
lastName: Optional[str] = Field(None, description="The last name of the row")
|
||||
preferredName: Optional[str] = Field(None, description="The preferred name of the row")
|
||||
|
||||
class CompanyInfo(BaseModel):
|
||||
name: str = Field(description="The name of the company")
|
||||
industry: str = Field(description="The industry of the company")
|
||||
|
||||
class SocialInfo(BaseModel):
|
||||
twitter: Optional[str] = Field(None, description="The Twitter username of the person")
|
||||
linkedin: Optional[str] = Field(None, description="The LinkedIn username of the person")
|
||||
facebook: Optional[str] = Field(None, description="The Facebook username of the person")
|
||||
instagram: Optional[str] = Field(None, description="The Instagram username of the person")
|
||||
|
||||
class RowEnrichmentResult(BaseModel):
|
||||
basicInfo: BasicInfo
|
||||
companyInfo: CompanyInfo
|
||||
socialInfo: SocialInfo
|
||||
|
||||
def complete_wait_token(wait_token: WaitToken, result: dict):
|
||||
"""Send the enrichment result back to Trigger.dev"""
|
||||
with trace("complete_wait_token"):
|
||||
url = f"{os.environ['TRIGGER_API_URL']}/api/v1/waitpoints/tokens/{wait_token.id}/complete"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {wait_token.publicAccessToken}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
response = requests.post(url, json={"data": result}, headers=headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
basic_info_agent = Agent(
|
||||
name="Basic Info Agent",
|
||||
instructions=f"""You are an expert at extracting basic information from a person's contact details.
|
||||
""",
|
||||
tools=[WebSearchTool()],
|
||||
output_type=BasicInfo
|
||||
)
|
||||
|
||||
company_info_agent = Agent(
|
||||
name="Company Info Agent",
|
||||
instructions=f"""You are an expert at extracting company information from a person's contact details.
|
||||
""",
|
||||
tools=[WebSearchTool()],
|
||||
output_type=CompanyInfo
|
||||
)
|
||||
|
||||
social_info_agent = Agent(
|
||||
name="Social Info Agent",
|
||||
instructions=f"""You are an expert at extracting social media information from a person's contact details.
|
||||
""",
|
||||
tools=[WebSearchTool()],
|
||||
output_type=SocialInfo
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="CSV Row Enrichment Agent",
|
||||
instructions=f"""You are an expert at enriching contact information data.
|
||||
Your task is to use web search to find additional information about a person
|
||||
based on their basic contact details.
|
||||
|
||||
Please find the basic information, company information, and social media information for the person.
|
||||
|
||||
The input data is from an unspecified CSV dataset, but most likely a CSV dump from a CRM or other source like Mailchimp, etc.
|
||||
|
||||
You'll receive the header row and a single row from the dataset to enrich, in their raw form.
|
||||
|
||||
Only include information you are confident about from reliable sources.
|
||||
Use null for fields where you cannot find reliable information.
|
||||
""",
|
||||
tools=[
|
||||
basic_info_agent.as_tool(
|
||||
tool_name="basic_info_agent",
|
||||
tool_description="Extract basic information from a person's contact details"
|
||||
),
|
||||
company_info_agent.as_tool(
|
||||
tool_name="company_info_agent",
|
||||
tool_description="Extract company information from a person's contact details"
|
||||
),
|
||||
social_info_agent.as_tool(
|
||||
tool_name="social_info_agent",
|
||||
tool_description="Extract social media information from a person's contact details"
|
||||
)
|
||||
],
|
||||
output_type=RowEnrichmentResult
|
||||
)
|
||||
|
||||
async def main(agent_input: AgentInput):
|
||||
# Run the agent
|
||||
result = await Runner.run(
|
||||
agent,
|
||||
f"""
|
||||
Header row: {agent_input.row.header}
|
||||
Row to enrich: {agent_input.row.row}
|
||||
CSV URL: {agent_input.row.url}
|
||||
"""
|
||||
)
|
||||
|
||||
enriched_data = result.final_output
|
||||
if isinstance(enriched_data, BaseModel):
|
||||
enriched_data = enriched_data.model_dump()
|
||||
|
||||
print("Final Output:")
|
||||
# Pretty print the final output
|
||||
print(json.dumps(enriched_data, indent=2))
|
||||
|
||||
if not agent_input.disableWaitTokenCompletion:
|
||||
try:
|
||||
# Send the result back to Trigger.dev
|
||||
complete_wait_token(agent_input.waitToken, enriched_data)
|
||||
|
||||
print("Successfully enriched data and notified Trigger.dev")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print("Error: Agent output was not valid JSON")
|
||||
sys.exit(1)
|
||||
|
||||
# Make sure to flush the logfire context
|
||||
logfire.force_flush()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Parse command line input as JSON
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python agent.py '<json_input>'")
|
||||
sys.exit(1)
|
||||
|
||||
# Extract the traceparent os.environ['TRACEPARENT']
|
||||
carrier ={'traceparent': os.environ['TRACEPARENT']}
|
||||
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
|
||||
|
||||
with attach_context(carrier=carrier, third_party=True):
|
||||
# Parse the input JSON into our Pydantic model
|
||||
input_data = AgentInput.model_validate_json(sys.argv[1])
|
||||
asyncio.run(main(input_data))
|
||||
@@ -0,0 +1,40 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const AgentLoopMetadata = z.object({
|
||||
waitToken: z.object({
|
||||
id: z.string(),
|
||||
publicAccessToken: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type AgentLoopMetadata = z.infer<typeof AgentLoopMetadata>;
|
||||
|
||||
export const CSVRowPayload = z.object({
|
||||
header: z.string().describe("The header of the CSV dataset"),
|
||||
row: z.string().describe("The row to handle"),
|
||||
url: z.string().url().describe("The URL of the CSV dataset"),
|
||||
});
|
||||
|
||||
export type CSVRowPayload = z.infer<typeof CSVRowPayload>;
|
||||
|
||||
export const RowEnrichmentResult = z.object({
|
||||
basicInfo: z.object({
|
||||
name: z.string().describe("The name of the row"),
|
||||
email: z.string().email().describe("The email of the row"),
|
||||
firstName: z.string().optional().describe("The first name of the row"),
|
||||
lastName: z.string().optional().describe("The last name of the row"),
|
||||
preferredName: z.string().optional().describe("The preferred name of the row"),
|
||||
}),
|
||||
companyInfo: z.object({
|
||||
name: z.string().describe("The name of the company"),
|
||||
industry: z.string().describe("The industry of the company"),
|
||||
}),
|
||||
socialInfo: z.object({
|
||||
twitter: z.string().url().optional().describe("The Twitter URL of the person"),
|
||||
linkedin: z.string().url().optional().describe("The LinkedIn URL of the person"),
|
||||
facebook: z.string().url().optional().describe("The Facebook URL of the person"),
|
||||
instagram: z.string().url().optional().describe("The Instagram URL of the person"),
|
||||
}),
|
||||
});
|
||||
|
||||
export type RowEnrichmentResult = z.infer<typeof RowEnrichmentResult>;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { defineConfig } from "@trigger.dev/sdk";
|
||||
import { pythonExtension } from "@trigger.dev/python/extension";
|
||||
import { installPlaywrightChromium } from "./src/extensions/playwright";
|
||||
|
||||
export default defineConfig({
|
||||
project: "proj_wkbbtayxrmeyqhankehb",
|
||||
dirs: ["./src/trigger"],
|
||||
maxDuration: 3600,
|
||||
build: {
|
||||
extensions: [
|
||||
// This is required to use the Python extension
|
||||
pythonExtension({
|
||||
requirementsFile: "./requirements.txt", // Optional: Path to your requirements file
|
||||
devPythonBinaryPath: `.venv/bin/python`, // Optional: Custom Python binary path
|
||||
scripts: ["src/trigger/python/**/*.py"], // List of Python scripts to include
|
||||
}),
|
||||
installPlaywrightChromium(),
|
||||
],
|
||||
},
|
||||
});
|
||||