diff --git a/docs/integrations.md b/docs/integrations.md index df491a18..ae0f1778 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -10,6 +10,11 @@ Bee Agent Framework is open-source framework for building, deploying, and servin import "dotenv/config"; import { DuckDuckGoSearch as LangChainDDG } from "@langchain/community/tools/duckduckgo_search"; import { createReactAgent as createLangGraphReactAgent } from "@langchain/langgraph/prebuilt"; +import type { + MessageContentComplex, + MessageContentImageUrl, + MessageContentText, +} from "@langchain/core/messages"; import { Workflow } from "bee-agent-framework/experimental/workflows/workflow"; import { z } from "zod"; import { createConsoleReader } from "examples/helpers/io.js"; @@ -22,6 +27,39 @@ import { UnconstrainedMemory } from "bee-agent-framework/memory/unconstrainedMem import { BaseMessage } from "bee-agent-framework/llms/primitives/message"; import { ChatMessage as LangChainMessage } from "@langchain/core/messages"; +//// LangGraph content parser helper functions //// + +function isMessageContentText(content: any): content is MessageContentText { + return content.type === "text"; +} + +function isMessageContentImageUrl(content: any): content is MessageContentImageUrl { + return content.type === "image_url"; +} + +function parseImageUrl(imageUrl: MessageContentImageUrl["image_url"]) { + if (typeof imageUrl === "string") { + return imageUrl; + } + + return imageUrl.url; +} + +function parseContent(content: MessageContentComplex[] | undefined | string) { + if (!content || typeof content === "string") { + return content; + } + + return content + .filter((c) => isMessageContentText(c) || isMessageContentImageUrl(c)) + .map((c) => { + return isMessageContentText(c) ? c.text : parseImageUrl(c.image_url); + }) + .join("\n"); +} + +//// workflow //// + const workflow = new Workflow({ schema: z.object({ memory: z.instanceof(ReadOnlyMemory), answer: z.string().default("") }), }) @@ -54,8 +92,9 @@ const workflow = new Workflow({ }, { signal: ctx.signal, recursionLimit: 5 }, ); - const answer = response.messages.at(-1)?.content; - return { next: Workflow.END, update: { answer: answer?.toString() } }; + const content = response.messages.at(-1)?.content; + + return { next: Workflow.END, update: { answer: parseContent(content) } }; }); const memory = new UnconstrainedMemory(); diff --git a/examples/integrations/langgraph.ts b/examples/integrations/langgraph.ts index 8e27b05b..36eb7ddf 100644 --- a/examples/integrations/langgraph.ts +++ b/examples/integrations/langgraph.ts @@ -1,6 +1,11 @@ import "dotenv/config"; import { DuckDuckGoSearch as LangChainDDG } from "@langchain/community/tools/duckduckgo_search"; import { createReactAgent as createLangGraphReactAgent } from "@langchain/langgraph/prebuilt"; +import type { + MessageContentComplex, + MessageContentImageUrl, + MessageContentText, +} from "@langchain/core/messages"; import { Workflow } from "bee-agent-framework/experimental/workflows/workflow"; import { z } from "zod"; import { createConsoleReader } from "examples/helpers/io.js"; @@ -13,6 +18,39 @@ import { UnconstrainedMemory } from "bee-agent-framework/memory/unconstrainedMem import { BaseMessage } from "bee-agent-framework/llms/primitives/message"; import { ChatMessage as LangChainMessage } from "@langchain/core/messages"; +//// LangGraph content parser helper functions //// + +function isMessageContentText(content: any): content is MessageContentText { + return content.type === "text"; +} + +function isMessageContentImageUrl(content: any): content is MessageContentImageUrl { + return content.type === "image_url"; +} + +function parseImageUrl(imageUrl: MessageContentImageUrl["image_url"]) { + if (typeof imageUrl === "string") { + return imageUrl; + } + + return imageUrl.url; +} + +function parseContent(content: MessageContentComplex[] | undefined | string) { + if (!content || typeof content === "string") { + return content; + } + + return content + .filter((c) => isMessageContentText(c) || isMessageContentImageUrl(c)) + .map((c) => { + return isMessageContentText(c) ? c.text : parseImageUrl(c.image_url); + }) + .join("\n"); +} + +//// workflow //// + const workflow = new Workflow({ schema: z.object({ memory: z.instanceof(ReadOnlyMemory), answer: z.string().default("") }), }) @@ -45,8 +83,9 @@ const workflow = new Workflow({ }, { signal: ctx.signal, recursionLimit: 5 }, ); - const answer = response.messages.at(-1)?.content; - return { next: Workflow.END, update: { answer: answer?.toString() } }; + const content = response.messages.at(-1)?.content; + + return { next: Workflow.END, update: { answer: parseContent(content) } }; }); const memory = new UnconstrainedMemory(); diff --git a/src/instrumentation/opentelemetry.spec.ts b/src/instrumentation/opentelemetry.spec.ts index 033c252d..7fda1763 100644 --- a/src/instrumentation/opentelemetry.spec.ts +++ b/src/instrumentation/opentelemetry.spec.ts @@ -45,7 +45,7 @@ export class CustomLLM extends LLM { throw new Error("Method not implemented."); } public readonly emitter = Emitter.root.child({ - namespace: ["bam", "llm"], + namespace: ["custom", "llm"], creator: this, }); meta(): Promise { diff --git a/src/instrumentation/opentelemetry.ts b/src/instrumentation/opentelemetry.ts index 0cfacf41..cbf6ff93 100644 --- a/src/instrumentation/opentelemetry.ts +++ b/src/instrumentation/opentelemetry.ts @@ -127,11 +127,12 @@ export function buildTraceTree({ ); } -export const isMeasurementedInstance = (instance: any) => - Boolean( +export function isMeasurementedInstance(instance: any) { + return Boolean( instance && (instance instanceof BaseAgent || instance instanceof Tool || instance instanceof BaseLLM), ); +} export function buildModuleUsageMetric({ instance }: BuildModuleUsageMetricProps) { moduleUsageCounter.add(1, { diff --git a/src/instrumentation/types.ts b/src/instrumentation/types.ts index dde29d21..82c857d4 100644 --- a/src/instrumentation/types.ts +++ b/src/instrumentation/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { api } from "@opentelemetry/sdk-node"; +import type { api } from "@opentelemetry/sdk-node"; export interface FrameworkSpan { attributes: {