From 9143bd8306cc40416a4b0bb9e5fd12b558c6b097 Mon Sep 17 00:00:00 2001 From: toyamarinyon Date: Thu, 2 Jan 2025 16:34:25 +0900 Subject: [PATCH 1/6] feat(playground): Integrate Langfuse telemetry and refactor - Update background image import to use shared component - Import `waitForLangfuseFlush` and implement it in node actions - Add LangfuseExporter for OpenTelemetry integration - Refactor execution telemetry logic to use experimental telemetry - Enhance Tailwind config paths to include packages - Upgrade langfuse dependencies in package.json These changes introduce telemetry integration via Langfuse, enhancing observability. LangfuseExporter is configured with a flushing mechanism to ensure timely data export. Refactoring improves performance by reducing overhead from previous Langfuse traces within execution logic. --- app/(playground)/p/[agentId]/page.tsx | 11 +++++++- bun.lockb | Bin 429564 -> 430740 bytes instrumentation.node.ts | 6 +++++ package.json | 1 + packages/lib/execution.ts | 36 +++++--------------------- packages/lib/utils.ts | 10 +++++++ 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/app/(playground)/p/[agentId]/page.tsx b/app/(playground)/p/[agentId]/page.tsx index 36956ace..b71affd2 100644 --- a/app/(playground)/p/[agentId]/page.tsx +++ b/app/(playground)/p/[agentId]/page.tsx @@ -42,6 +42,7 @@ import { buildGraphExecutionPath, buildGraphFolderPath, createGithubIntegrationSettingId, + waitForLangfuseFlush, } from "@giselles-ai/lib/utils"; import type { AgentId, @@ -59,6 +60,7 @@ import { del, list, put } from "@vercel/blob"; import { ReactFlowProvider } from "@xyflow/react"; import { eq } from "drizzle-orm"; import { notFound } from "next/navigation"; +import { after } from "next/server"; // Extend the max duration of the server actions from this page to 5 minutes // https://vercel.com/docs/functions/runtimes#max-duration @@ -228,7 +230,14 @@ export default async function Page({ async function executeNodeAction(executionId: ExecutionId, nodeId: NodeId) { "use server"; - return await executeNode({ agentId, executionId, nodeId, stream: true }); + const response = await executeNode({ + agentId, + executionId, + nodeId, + stream: true, + }); + after(waitForLangfuseFlush); + return response; } async function onFinishPerformExecutionAction( diff --git a/bun.lockb b/bun.lockb index c4b11b9a61b88b8683199ad5eda61eb197a1a44d..12226c00184c9633f243671a1bcf2de7c74605b5 100755 GIT binary patch delta 78897 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 delta 79035 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 I&mQyt0OTg41poj5 diff --git a/instrumentation.node.ts b/instrumentation.node.ts index ade3aaf1..5f17dc5e 100644 --- a/instrumentation.node.ts +++ b/instrumentation.node.ts @@ -4,10 +4,16 @@ import { noopSpanProcessor, } from "@/lib/opentelemetry"; import { registerOTel } from "@vercel/otel"; +import { LangfuseExporter } from "langfuse-vercel"; registerOTel({ serviceName: "giselle", spanProcessors: [noopSpanProcessor], metricReader, logRecordProcessor, + traceExporter: new LangfuseExporter({ + flushInterval: Number.parseInt( + process.env.LANGFUSE_FLUSH_INTERVAL ?? "1000", + ), + }), }); diff --git a/package.json b/package.json index c1f39260..1d077911 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "import-in-the-middle": "1.11.0", "input-otp": "^1.2.4", "langfuse": "3.26.0", + "langfuse-vercel": "3.32.0", "lucide-react": "0.417.0", "next": "^15.1.2", "next-auth": "^5.0.0-beta.20", diff --git a/packages/lib/execution.ts b/packages/lib/execution.ts index 32bb8bd3..666f5065 100644 --- a/packages/lib/execution.ts +++ b/packages/lib/execution.ts @@ -23,7 +23,6 @@ import { createStreamableValue } from "ai/rsc"; import { MockLanguageModelV1, simulateReadableStream } from "ai/test"; import { and, eq } from "drizzle-orm"; import HandleBars from "handlebars"; -import Langfuse from "langfuse"; import * as v from "valibot"; import type { AgentId, @@ -285,10 +284,6 @@ async function performFlowExecution( throw new AgentTimeNotAvailableError(); } const startTime = Date.now(); - const lf = new Langfuse(); - const trace = lf.trace({ - sessionId: context.executionId, - }); const node = context.node; switch (node.content.type) { @@ -310,20 +305,6 @@ async function performFlowExecution( const topP = node.content.topP; const temperature = node.content.temperature; - trace.update({ - input: prompt, - }); - - const generationTracer = trace.generation({ - name: "generate-text", - input: prompt, - model: langfuseModel(node.content.llm), - modelParameters: { - topP: node.content.topP, - temperature: node.content.temperature, - }, - }); - if (context.stream) { const streamableValue = createStreamableValue(); (async () => { @@ -353,9 +334,6 @@ async function performFlowExecution( await withTokenMeasurement( createLogger(node.content.type), async () => { - generationTracer.end({ output: result }); - trace.update({ output: result }); - await lf.shutdownAsync(); waitForTelemetryExport(); return { usage: await usage }; }, @@ -364,10 +342,6 @@ async function performFlowExecution( ); streamableValue.done(); })().catch((error) => { - generationTracer.update({ - level: "ERROR", - statusMessage: toErrorWithMessage(error).message, - }); streamableValue.error(error); }); @@ -381,14 +355,18 @@ async function performFlowExecution( ), topP, temperature, + experimental_telemetry: { + isEnabled: true, + functionId: "giselles-ai.lib.performFlowExecution", + metadata: { + sessionId: context.executionId, + }, + }, }); waitUntil( withTokenMeasurement( createLogger(node.content.type), async () => { - generationTracer.end({ output: object }); - trace.update({ output: object }); - await lf.shutdownAsync(); waitForTelemetryExport(); return { usage }; }, diff --git a/packages/lib/utils.ts b/packages/lib/utils.ts index 49e88c2c..a478e946 100644 --- a/packages/lib/utils.ts +++ b/packages/lib/utils.ts @@ -410,3 +410,13 @@ export function isStreamableValue(value: unknown): value is StreamableValue { value.type === STREAMABLE_VALUE_TYPE ); } + +const otelBspScheduleDelay = Number.parseInt( + process.env.OTEL_BSP_SCHEDULE_DELAY ?? "5000", +); +const langfuseFlushInterval = Number.parseInt( + process.env.LANGFUSE_FLUSH_INTERVAL ?? "1000", +); +export const waitForLangfuseFlush = new Promise((resolve) => + setTimeout(resolve, otelBspScheduleDelay + langfuseFlushInterval), +); From ab9fa2917135106a46baeb836f536255d095ef22 Mon Sep 17 00:00:00 2001 From: toyamarinyon Date: Thu, 2 Jan 2025 23:14:04 +0900 Subject: [PATCH 2/6] refactor(graph): Extract persistGraph to separate module - Moved persistGraph logic from page.tsx to a new module at giselle-provider/graph/persist.ts for better modularity - Replaced inline persistGraph logic with import from new module - Simplified graph handling in page.tsx by outsourcing blob deletion and graph URL update functionality This refactor should make the codebase more maintainable by adhering to the single responsibility principle, without introducing breaking changes. --- app/(playground)/p/[agentId]/page.tsx | 75 +++-------------- packages/giselle-provider/graph/persist.ts | 97 ++++++++++++++++++++++ 2 files changed, 107 insertions(+), 65 deletions(-) create mode 100644 packages/giselle-provider/graph/persist.ts diff --git a/app/(playground)/p/[agentId]/page.tsx b/app/(playground)/p/[agentId]/page.tsx index b71affd2..17b70cbe 100644 --- a/app/(playground)/p/[agentId]/page.tsx +++ b/app/(playground)/p/[agentId]/page.tsx @@ -16,7 +16,6 @@ import type { GitHubTriggerEvent, } from "@/services/external/github/types"; import { reportAgentTimeUsage } from "@/services/usage-based-billing/report-agent-time-usage"; -import { putGraph } from "@giselles-ai/actions"; import { Playground } from "@giselles-ai/components/playground"; import { AgentNameProvider } from "@giselles-ai/contexts/agent-name"; import { DeveloperModeProvider } from "@giselles-ai/contexts/developer-mode"; @@ -28,6 +27,7 @@ import { PlaygroundModeProvider } from "@giselles-ai/contexts/playground-mode"; import { PropertiesPanelProvider } from "@giselles-ai/contexts/properties-panel"; import { ToastProvider } from "@giselles-ai/contexts/toast"; import { ToolbarContextProvider } from "@giselles-ai/contexts/toolbar"; +import { persistGraph } from "@giselles-ai/giselle-provider/graph/persist"; import { executeNode, executeStep, @@ -40,9 +40,7 @@ import { import { isLatestVersion, migrateGraph } from "@giselles-ai/lib/graph"; import { buildGraphExecutionPath, - buildGraphFolderPath, createGithubIntegrationSettingId, - waitForLangfuseFlush, } from "@giselles-ai/lib/utils"; import type { AgentId, @@ -56,7 +54,7 @@ import type { NodeId, StepId, } from "@giselles-ai/types"; -import { del, list, put } from "@vercel/blob"; +import { put } from "@vercel/blob"; import { ReactFlowProvider } from "@xyflow/react"; import { eq } from "drizzle-orm"; import { notFound } from "next/navigation"; @@ -91,70 +89,17 @@ export default async function Page({ let graph = await fetch(agent.graphUrl).then( (res) => res.json() as unknown as Graph, ); + let graphUrl = agent.graphUrl; + if (!isLatestVersion(graph)) { + graph = migrateGraph(graph); + graphUrl = await persistGraph({ graph, agentId }); + } const gitHubIntegrationState = await getGitHubIntegrationState(agent.dbId); - async function persistGraph(graph: Graph) { + async function persistGraphAction(graph: Graph) { "use server"; - const startTime = Date.now(); - const logger = createLogger("persistGraph"); - const { url } = await putGraph(graph); - const { blobList } = await withCountMeasurement( - logger, - async () => { - const result = await list({ - prefix: buildGraphFolderPath(graph.id), - mode: "folded", - }); - const size = result.blobs.reduce((sum, blob) => sum + blob.size, 0); - return { - blobList: result, - size, - }; - }, - ExternalServiceName.VercelBlob, - startTime, - VercelBlobOperation.List, - ); - - const oldBlobs = blobList.blobs - .filter((blob) => blob.url !== url) - .map((blob) => ({ - url: blob.url, - size: blob.size, - })); - - if (oldBlobs.length > 0) { - await withCountMeasurement( - logger, - async () => { - await del(oldBlobs.map((blob) => blob.url)); - const totalSize = oldBlobs.reduce((sum, blob) => sum + blob.size, 0); - return { - size: totalSize, - }; - }, - ExternalServiceName.VercelBlob, - startTime, - VercelBlobOperation.Del, - ); - waitForTelemetryExport(); - } - - await db - .update(agents) - .set({ - graphUrl: url, - }) - .where(eq(agents.id, agentId)); - - return url; - } - - let graphUrl = agent.graphUrl; - if (!isLatestVersion(graph)) { - graph = migrateGraph(graph); - graphUrl = await persistGraph(graph); + return persistGraph({ graph, agentId }); } async function updateAgentName(agentName: string) { @@ -360,7 +305,7 @@ export default async function Page({ { + const result = await put(buildGraphPath(graph.id), stringifiedGraph, { + access: "public", + }); + + return { + blob: result, + size: new TextEncoder().encode(stringifiedGraph).length, + }; + }, + ExternalServiceName.VercelBlob, + startTime, + VercelBlobOperation.Put, + ); + waitForTelemetryExport(); + return result.blob; +} + +export async function persistGraph({ + graph, + agentId, +}: { graph: Graph; agentId: AgentId }) { + const startTime = Date.now(); + const { url } = await putGraph(graph); + + await db + .update(agents) + .set({ + graphUrl: url, + }) + .where(eq(agents.id, agentId)); + + const logger = createLogger("persistGraph"); + const { blobList } = await withCountMeasurement( + logger, + async () => { + const result = await list({ + prefix: buildGraphFolderPath(graph.id), + mode: "folded", + }); + const size = result.blobs.reduce((sum, blob) => sum + blob.size, 0); + return { + blobList: result, + size, + }; + }, + ExternalServiceName.VercelBlob, + startTime, + VercelBlobOperation.List, + ); + + const oldBlobs = blobList.blobs + .filter((blob) => blob.url !== url) + .map((blob) => ({ + url: blob.url, + size: blob.size, + })); + + if (oldBlobs.length > 0) { + await withCountMeasurement( + logger, + async () => { + await del(oldBlobs.map((blob) => blob.url)); + const totalSize = oldBlobs.reduce((sum, blob) => sum + blob.size, 0); + return { + size: totalSize, + }; + }, + ExternalServiceName.VercelBlob, + startTime, + VercelBlobOperation.Del, + ); + waitForTelemetryExport(); + } + + return url; +} From b59ffcd17706882a4b0bde936c68865b7b9ff8e7 Mon Sep 17 00:00:00 2001 From: toyamarinyon Date: Thu, 2 Jan 2025 23:15:26 +0900 Subject: [PATCH 3/6] refactor(server): Update waitForLangfuseFlush invocation - Change waitForLangfuseFlush from a promise to a function that returns a promise in utils.ts for enhanced laziness - Remove redundant import of after from page.tsx - Call waitForLangfuseFlush using `after` in execution.ts to ensure proper scheduling of post-execution logic This update aims to address more efficient handling of post-execution scheduling and resolve potential memory overhead by improving how flush timing is managed. No breaking changes introduced. --- app/(playground)/p/[agentId]/page.tsx | 2 -- packages/lib/execution.ts | 16 +++++++++++++++- packages/lib/utils.ts | 7 ++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/(playground)/p/[agentId]/page.tsx b/app/(playground)/p/[agentId]/page.tsx index 17b70cbe..aa504503 100644 --- a/app/(playground)/p/[agentId]/page.tsx +++ b/app/(playground)/p/[agentId]/page.tsx @@ -58,7 +58,6 @@ import { put } from "@vercel/blob"; import { ReactFlowProvider } from "@xyflow/react"; import { eq } from "drizzle-orm"; import { notFound } from "next/navigation"; -import { after } from "next/server"; // Extend the max duration of the server actions from this page to 5 minutes // https://vercel.com/docs/functions/runtimes#max-duration @@ -181,7 +180,6 @@ export default async function Page({ nodeId, stream: true, }); - after(waitForLangfuseFlush); return response; } diff --git a/packages/lib/execution.ts b/packages/lib/execution.ts index 666f5065..68695638 100644 --- a/packages/lib/execution.ts +++ b/packages/lib/execution.ts @@ -23,6 +23,7 @@ import { createStreamableValue } from "ai/rsc"; import { MockLanguageModelV1, simulateReadableStream } from "ai/test"; import { and, eq } from "drizzle-orm"; import HandleBars from "handlebars"; +import { after } from "next/server"; import * as v from "valibot"; import type { AgentId, @@ -43,7 +44,11 @@ import type { } from "../types"; import { AgentTimeNotAvailableError } from "./errors"; import { textGenerationPrompt } from "./prompts"; -import { langfuseModel, toErrorWithMessage } from "./utils"; +import { + langfuseModel, + toErrorWithMessage, + waitForLangfuseFlush, +} from "./utils"; function resolveLanguageModel( llm: TextGenerateActionContent["llm"], @@ -316,6 +321,13 @@ async function performFlowExecution( ), topP, temperature, + experimental_telemetry: { + isEnabled: true, + functionId: "giselles-ai.lib.performFlowExecution", + metadata: { + sessionId: context.executionId, + }, + }, }); for await (const partialObject of partialObjectStream) { @@ -340,6 +352,7 @@ async function performFlowExecution( model, startTime, ); + after(waitForLangfuseFlush); streamableValue.done(); })().catch((error) => { streamableValue.error(error); @@ -374,6 +387,7 @@ async function performFlowExecution( startTime, ), ); + after(waitForLangfuseFlush); return { type: "text", title: object.title, diff --git a/packages/lib/utils.ts b/packages/lib/utils.ts index a478e946..dc6d8b21 100644 --- a/packages/lib/utils.ts +++ b/packages/lib/utils.ts @@ -417,6 +417,7 @@ const otelBspScheduleDelay = Number.parseInt( const langfuseFlushInterval = Number.parseInt( process.env.LANGFUSE_FLUSH_INTERVAL ?? "1000", ); -export const waitForLangfuseFlush = new Promise((resolve) => - setTimeout(resolve, otelBspScheduleDelay + langfuseFlushInterval), -); +export const waitForLangfuseFlush = () => + new Promise((resolve) => + setTimeout(resolve, otelBspScheduleDelay + langfuseFlushInterval), + ); From 3b0c297689cd335fe6c3d6b9ce9cdd06ac6c54a4 Mon Sep 17 00:00:00 2001 From: toyamarinyon Date: Fri, 3 Jan 2025 00:06:11 +0900 Subject: [PATCH 4/6] feat(telemetry): Add userId to execution context - Introduce userId to the execution context for enhanced telemetry data, allowing better user-specific tracking and debugging - Modify performFlowExecution to include userId in experimental telemetry metadata - Implement parseExecutionContextToTelemetryMetadata to structure metadata, ensuring consistent formatting across different telemetry actions This change aims to improve telemetry insights by associating user identifiers with execution flows, thereby assisting in user behavior analysis and debugging. No breaking changes introduced. --- app/(playground)/p/[agentId]/page.tsx | 1 + packages/lib/execution.ts | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/(playground)/p/[agentId]/page.tsx b/app/(playground)/p/[agentId]/page.tsx index aa504503..f69b7395 100644 --- a/app/(playground)/p/[agentId]/page.tsx +++ b/app/(playground)/p/[agentId]/page.tsx @@ -179,6 +179,7 @@ export default async function Page({ executionId, nodeId, stream: true, + userId: user.id, }); return response; } diff --git a/packages/lib/execution.ts b/packages/lib/execution.ts index 68695638..da0df43e 100644 --- a/packages/lib/execution.ts +++ b/packages/lib/execution.ts @@ -279,6 +279,7 @@ interface ExecutionContext { nodes: Node[]; connections: Connection[]; stream?: boolean; + userId?: string; } async function performFlowExecution( @@ -324,9 +325,7 @@ async function performFlowExecution( experimental_telemetry: { isEnabled: true, functionId: "giselles-ai.lib.performFlowExecution", - metadata: { - sessionId: context.executionId, - }, + metadata: parseExecutionContextToTelemetryMetadata(context), }, }); @@ -370,10 +369,8 @@ async function performFlowExecution( temperature, experimental_telemetry: { isEnabled: true, - functionId: "giselles-ai.lib.performFlowExecution", - metadata: { - sessionId: context.executionId, - }, + functionId: "giselles-ai.lib.performFlowExecution.generateObject", + metadata: parseExecutionContextToTelemetryMetadata(context), }, }); waitUntil( @@ -550,12 +547,14 @@ interface ExecuteNodeParams { executionId: ExecutionId; nodeId: NodeId; stream?: boolean; + userId?: string; } export async function executeNode({ agentId, executionId, nodeId, stream, + userId, }: ExecuteNodeParams) { const agent = await db.query.agents.findFirst({ where: (agents, { eq }) => eq(agents.id, agentId), @@ -582,6 +581,7 @@ export async function executeNode({ nodes: graph.nodes, connections: graph.connections, stream, + userId, }; return performFlowExecution(context); @@ -615,3 +615,13 @@ async function canPerformFlowExecution(agentId: AgentId) { const team = res[0]; return await isAgentTimeAvailable(team); } + +function parseExecutionContextToTelemetryMetadata( + executionContext: ExecutionContext, +): Record { + return { + sessionId: executionContext.executionId, + agentId: executionContext.agentId, + ...(executionContext.userId && { userId: executionContext.userId }), + }; +} From 57254815059f38a9f39564a70b671f05a50ef610 Mon Sep 17 00:00:00 2001 From: toyamarinyon Date: Fri, 3 Jan 2025 00:12:31 +0900 Subject: [PATCH 5/6] feat(user-tracking): Include userId in execution params - Add userId to execution parameters for executeStep and retryStep functions to capture user-specific telemetry data - Modify page functions to pass userId from the user context for more granular tracking and user-driven analytics - Ensure all execution paths maintain consistent userId propagation for future scalability in user data insights This enhancement improves the ability to trace executions back to specific users, enabling better analytics and debugging capabilities with no breaking changes introduced. --- app/(playground)/p/[agentId]/page.tsx | 5 +++-- packages/lib/execution.ts | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/(playground)/p/[agentId]/page.tsx b/app/(playground)/p/[agentId]/page.tsx index f69b7395..af48ea25 100644 --- a/app/(playground)/p/[agentId]/page.tsx +++ b/app/(playground)/p/[agentId]/page.tsx @@ -126,6 +126,7 @@ export default async function Page({ stepId, artifacts, stream: true, + userId: user.id, }); } async function putExecutionAction(executionSnapshot: ExecutionSnapshot) { @@ -169,19 +170,19 @@ export default async function Page({ stepId, artifacts, stream: true, + userId: user.id, }); } async function executeNodeAction(executionId: ExecutionId, nodeId: NodeId) { "use server"; - const response = await executeNode({ + return await executeNode({ agentId, executionId, nodeId, stream: true, userId: user.id, }); - return response; } async function onFinishPerformExecutionAction( diff --git a/packages/lib/execution.ts b/packages/lib/execution.ts index da0df43e..11a42ebc 100644 --- a/packages/lib/execution.ts +++ b/packages/lib/execution.ts @@ -411,6 +411,7 @@ interface ExecuteStepParams { stepId: StepId; artifacts: Artifact[]; stream?: boolean; + userId?: string; overrideData?: OverrideData[]; } export async function executeStep({ @@ -420,6 +421,7 @@ export async function executeStep({ stepId, artifacts, stream, + userId, overrideData, }: ExecuteStepParams) { const agent = await db.query.agents.findFirst({ @@ -490,6 +492,7 @@ export async function executeStep({ artifacts, nodes: graph.nodes, connections: graph.connections, + userId, stream, }; @@ -503,6 +506,7 @@ interface RetryStepParams { stepId: StepId; artifacts: Artifact[]; stream?: boolean; + userId?: string; } export async function retryStep({ agentId, @@ -511,6 +515,7 @@ export async function retryStep({ stepId, artifacts, stream, + userId, }: RetryStepParams) { const executionSnapshot = await fetch(retryExecutionSnapshotUrl).then( (res) => res.json() as unknown as ExecutionSnapshot, @@ -537,6 +542,7 @@ export async function retryStep({ nodes: executionSnapshot.nodes, connections: executionSnapshot.connections, stream, + userId, }; return performFlowExecution(context); From 83d856f6df2834c389bb1722d8487538c015860d Mon Sep 17 00:00:00 2001 From: satoshi toyama Date: Mon, 6 Jan 2025 13:46:26 +0900 Subject: [PATCH 6/6] add OTEL_BSP_SCHEDULE_DELAY to the example --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index 2a117e54..35d248cc 100644 --- a/.env.example +++ b/.env.example @@ -43,6 +43,10 @@ LANGFUSE_BASEURL= LANGFUSE_PUBLIC_KEY= LANGFUSE_SECRET_KEY= +# Configure delay for tracer that set vercel/otel +# @see https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#batch-span-processor +# OTEL_BSP_SCHEDULE_DELAY= + # OpenAI API https://openai.com/index/openai-api/ # @see https://platform.openai.com/docs/quickstart OPENAI_API_KEY=