From 7d061d9e84ddf77d1007d2da13efcaf207b1b18f Mon Sep 17 00:00:00 2001 From: Ran Shemtov Date: Mon, 17 Feb 2025 10:44:43 +0100 Subject: [PATCH] feat(configurable): execute langgraph with user config (#1376) --- CopilotKit/.changeset/polite-falcons-melt.md | 6 ++++++ .../packages/react-core/src/hooks/use-chat.ts | 1 + .../packages/react-core/src/hooks/use-coagent.ts | 13 +++++++++++++ .../packages/react-core/src/types/coagent-state.ts | 1 + .../src/graphql/inputs/agent-state.input.ts | 3 +++ .../src/lib/runtime/remote-action-constructors.ts | 14 ++++++++++---- .../runtime/src/lib/runtime/remote-lg-action.ts | 5 +++++ sdk-python/copilotkit/agent.py | 1 + sdk-python/copilotkit/integrations/fastapi.py | 4 ++++ sdk-python/copilotkit/langgraph_agent.py | 5 ++++- sdk-python/copilotkit/sdk.py | 3 +++ 11 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 CopilotKit/.changeset/polite-falcons-melt.md diff --git a/CopilotKit/.changeset/polite-falcons-melt.md b/CopilotKit/.changeset/polite-falcons-melt.md new file mode 100644 index 0000000000..b9c475c419 --- /dev/null +++ b/CopilotKit/.changeset/polite-falcons-melt.md @@ -0,0 +1,6 @@ +--- +"@copilotkit/react-core": minor +"@copilotkit/runtime": minor +--- + +- feat(configurable): execute langgraph with user config diff --git a/CopilotKit/packages/react-core/src/hooks/use-chat.ts b/CopilotKit/packages/react-core/src/hooks/use-chat.ts index 06d4882a94..f65d59a68b 100644 --- a/CopilotKit/packages/react-core/src/hooks/use-chat.ts +++ b/CopilotKit/packages/react-core/src/hooks/use-chat.ts @@ -318,6 +318,7 @@ export function useChat(options: UseChatOptions): UseChatHelpers { agentStates: Object.values(coagentStatesRef.current!).map((state) => ({ agentName: state.name, state: JSON.stringify(state.state), + configurable: JSON.stringify(state.configurable ?? {}), })), forwardedParameters: options.forwardedParameters || {}, }, diff --git a/CopilotKit/packages/react-core/src/hooks/use-coagent.ts b/CopilotKit/packages/react-core/src/hooks/use-coagent.ts index ca7772a7a2..3a11898cc2 100644 --- a/CopilotKit/packages/react-core/src/hooks/use-coagent.ts +++ b/CopilotKit/packages/react-core/src/hooks/use-coagent.ts @@ -111,6 +111,10 @@ interface WithInternalStateManagementAndInitial { * The initial state of the agent. */ initialState: T; + /** + * Config to pass to a LangGraph Agent + */ + configurable?: Record; } interface WithInternalStateManagement { @@ -122,6 +126,10 @@ interface WithInternalStateManagement { * Optional initialState with default type any */ initialState?: any; + /** + * Config to pass to a LangGraph Agent + */ + configurable?: Record; } interface WithExternalStateManagement { @@ -137,6 +145,10 @@ interface WithExternalStateManagement { * A function to update the state of the agent. */ setState: (newState: T | ((prevState: T | undefined) => T)) => void; + /** + * Config to pass to a LangGraph Agent + */ + configurable?: Record; } type UseCoagentOptions = @@ -244,6 +256,7 @@ export function useCoAgent(options: UseCoagentOptions): UseCoagentRe return { name, state: isInternalStateManagementWithInitial(options) ? options.initialState : {}, + configurable: options.configurable ?? {}, running: false, active: false, threadId: undefined, diff --git a/CopilotKit/packages/react-core/src/types/coagent-state.ts b/CopilotKit/packages/react-core/src/types/coagent-state.ts index 0814996d02..286624b1bb 100644 --- a/CopilotKit/packages/react-core/src/types/coagent-state.ts +++ b/CopilotKit/packages/react-core/src/types/coagent-state.ts @@ -4,6 +4,7 @@ export interface CoagentState { running: boolean; active: boolean; threadId?: string; + configurable?: Record; nodeName?: string; runId?: string; } diff --git a/CopilotKit/packages/runtime/src/graphql/inputs/agent-state.input.ts b/CopilotKit/packages/runtime/src/graphql/inputs/agent-state.input.ts index b2033e1d89..d45f9734d3 100644 --- a/CopilotKit/packages/runtime/src/graphql/inputs/agent-state.input.ts +++ b/CopilotKit/packages/runtime/src/graphql/inputs/agent-state.input.ts @@ -7,4 +7,7 @@ export class AgentStateInput { @Field(() => String) state: string; + + @Field(() => String, { nullable: true }) + configurable?: string; } diff --git a/CopilotKit/packages/runtime/src/lib/runtime/remote-action-constructors.ts b/CopilotKit/packages/runtime/src/lib/runtime/remote-action-constructors.ts index 3901a920f4..739e19b09b 100644 --- a/CopilotKit/packages/runtime/src/lib/runtime/remote-action-constructors.ts +++ b/CopilotKit/packages/runtime/src/lib/runtime/remote-action-constructors.ts @@ -55,10 +55,12 @@ export function constructLGCRemoteAction({ }); let state = {}; + let configurable = {}; if (agentStates) { - const jsonState = agentStates.find((state) => state.agentName === name)?.state; + const jsonState = agentStates.find((state) => state.agentName === name); if (jsonState) { - state = JSON.parse(jsonState); + state = JSON.parse(jsonState.state); + configurable = JSON.parse(jsonState.configurable); } } @@ -72,6 +74,7 @@ export function constructLGCRemoteAction({ nodeName, messages: [...messages, ...additionalMessages], state, + configurable, properties: graphqlContext.properties, actions: actionInputsWithoutAgents.map((action) => ({ name: action.name, @@ -196,10 +199,12 @@ export function constructRemoteActions({ }); let state = {}; + let configurable = {}; if (agentStates) { - const jsonState = agentStates.find((state) => state.agentName === name)?.state; + const jsonState = agentStates.find((state) => state.agentName === name); if (jsonState) { - state = JSON.parse(jsonState); + state = JSON.parse(jsonState.state); + configurable = JSON.parse(jsonState.configurable); } } @@ -214,6 +219,7 @@ export function constructRemoteActions({ nodeName, messages: [...messages, ...additionalMessages], state, + configurable, properties: graphqlContext.properties, actions: actionInputsWithoutAgents.map((action) => ({ name: action.name, diff --git a/CopilotKit/packages/runtime/src/lib/runtime/remote-lg-action.ts b/CopilotKit/packages/runtime/src/lib/runtime/remote-lg-action.ts index 3f6dacc7e2..4418da00b4 100644 --- a/CopilotKit/packages/runtime/src/lib/runtime/remote-lg-action.ts +++ b/CopilotKit/packages/runtime/src/lib/runtime/remote-lg-action.ts @@ -24,6 +24,7 @@ interface ExecutionArgs extends Omit { nodeName: string; messages: Message[]; state: State; + configurable?: Record; properties: CopilotRequestContextProperties; actions: ExecutionAction[]; logger: Logger; @@ -87,6 +88,7 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E agent, nodeName: initialNodeName, state: initialState, + configurable, messages, actions, logger, @@ -202,6 +204,9 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E } const assistantId = retrievedAssistant.assistant_id; + if (configurable) { + await client.assistants.update(assistantId, { config: { configurable } }); + } const graphInfo = await client.assistants.getGraph(assistantId); let streamingStateExtractor = new StreamingStateExtractor([]); diff --git a/sdk-python/copilotkit/agent.py b/sdk-python/copilotkit/agent.py index 6a7a87e12a..59f94949e0 100644 --- a/sdk-python/copilotkit/agent.py +++ b/sdk-python/copilotkit/agent.py @@ -27,6 +27,7 @@ class Agent(ABC): self, *, state: dict, + configurable: Optional[dict] = None, messages: List[Message], thread_id: Optional[str] = None, node_name: Optional[str] = None, diff --git a/sdk-python/copilotkit/integrations/fastapi.py b/sdk-python/copilotkit/integrations/fastapi.py index b890ab9745..54ecc144bf 100644 --- a/sdk-python/copilotkit/integrations/fastapi.py +++ b/sdk-python/copilotkit/integrations/fastapi.py @@ -103,6 +103,7 @@ async def handler(request: Request, sdk: CopilotKitRemoteEndpoint): if method == 'POST' and path == 'agents/execute': thread_id = body.get("threadId") node_name = body.get("nodeName") + configurable = body.get("configurable") name = body_get_or_raise(body, "name") state = body_get_or_raise(body, "state") @@ -117,6 +118,7 @@ async def handler(request: Request, sdk: CopilotKitRemoteEndpoint): node_name=node_name, name=name, state=state, + configurable=configurable, messages=messages, actions=actions, meta_events=meta_events, @@ -174,6 +176,7 @@ def handle_execute_agent( # pylint: disable=too-many-arguments node_name: str, name: str, state: dict, + configurable: Optional[dict] = None, messages: List[Message], actions: List[ActionDict], meta_events: Optional[List[MetaEvent]] = None, @@ -186,6 +189,7 @@ def handle_execute_agent( # pylint: disable=too-many-arguments name=name, node_name=node_name, state=state, + configurable=configurable, messages=messages, actions=actions, meta_events=meta_events, diff --git a/sdk-python/copilotkit/langgraph_agent.py b/sdk-python/copilotkit/langgraph_agent.py index 9bff065c08..0e2eb9128b 100644 --- a/sdk-python/copilotkit/langgraph_agent.py +++ b/sdk-python/copilotkit/langgraph_agent.py @@ -217,6 +217,7 @@ class LangGraphAgent(Agent): self, *, state: dict, + configurable: Optional[dict] = None, messages: List[Message], thread_id: Optional[str] = None, node_name: Optional[str] = None, @@ -225,6 +226,7 @@ class LangGraphAgent(Agent): ): return self._stream_events( state=state, + configurable=configurable, messages=messages, actions=actions, thread_id=thread_id, @@ -236,6 +238,7 @@ class LangGraphAgent(Agent): self, *, state: Any, + configurable: Optional[dict] = None, messages: List[Message], thread_id: str, actions: Optional[List[ActionDict]] = None, @@ -244,7 +247,7 @@ class LangGraphAgent(Agent): ): config = ensure_config(cast(Any, self.langgraph_config.copy()) if self.langgraph_config else {}) # pylint: disable=line-too-long - config["configurable"] = config.get("configurable", {}) + config["configurable"] = {**config.get("configurable", {}), **(configurable or {})} config["configurable"]["thread_id"] = thread_id agent_state = await self.graph.aget_state(config) diff --git a/sdk-python/copilotkit/sdk.py b/sdk-python/copilotkit/sdk.py index 713cff74c1..3f57b558b7 100644 --- a/sdk-python/copilotkit/sdk.py +++ b/sdk-python/copilotkit/sdk.py @@ -264,6 +264,7 @@ class CopilotKitRemoteEndpoint: thread_id: str, node_name: str, state: dict, + configurable: Optional[dict] = None, messages: List[Message], actions: List[ActionDict], meta_events: Optional[List[MetaEvent]] = None, @@ -284,6 +285,7 @@ class CopilotKitRemoteEndpoint: ("Thread ID", thread_id), ("Node Name", node_name), ("State", state), + ("Configurable", configurable), ("Messages", messages), ("Actions", actions), ("MetaEvents", meta_events), @@ -295,6 +297,7 @@ class CopilotKitRemoteEndpoint: thread_id=thread_id, node_name=node_name, state=state, + configurable=configurable, messages=messages, actions=actions, meta_events=meta_events