Skip to content

Commit 7d061d9

Browse files
authored
feat(configurable): execute langgraph with user config (CopilotKit#1376)
1 parent 3e4d3b8 commit 7d061d9

11 files changed

Lines changed: 51 additions & 5 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@copilotkit/react-core": minor
3+
"@copilotkit/runtime": minor
4+
---
5+
6+
- feat(configurable): execute langgraph with user config

CopilotKit/packages/react-core/src/hooks/use-chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ export function useChat(options: UseChatOptions): UseChatHelpers {
318318
agentStates: Object.values(coagentStatesRef.current!).map((state) => ({
319319
agentName: state.name,
320320
state: JSON.stringify(state.state),
321+
configurable: JSON.stringify(state.configurable ?? {}),
321322
})),
322323
forwardedParameters: options.forwardedParameters || {},
323324
},

CopilotKit/packages/react-core/src/hooks/use-coagent.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ interface WithInternalStateManagementAndInitial<T> {
111111
* The initial state of the agent.
112112
*/
113113
initialState: T;
114+
/**
115+
* Config to pass to a LangGraph Agent
116+
*/
117+
configurable?: Record<string, any>;
114118
}
115119

116120
interface WithInternalStateManagement {
@@ -122,6 +126,10 @@ interface WithInternalStateManagement {
122126
* Optional initialState with default type any
123127
*/
124128
initialState?: any;
129+
/**
130+
* Config to pass to a LangGraph Agent
131+
*/
132+
configurable?: Record<string, any>;
125133
}
126134

127135
interface WithExternalStateManagement<T> {
@@ -137,6 +145,10 @@ interface WithExternalStateManagement<T> {
137145
* A function to update the state of the agent.
138146
*/
139147
setState: (newState: T | ((prevState: T | undefined) => T)) => void;
148+
/**
149+
* Config to pass to a LangGraph Agent
150+
*/
151+
configurable?: Record<string, any>;
140152
}
141153

142154
type UseCoagentOptions<T> =
@@ -244,6 +256,7 @@ export function useCoAgent<T = any>(options: UseCoagentOptions<T>): UseCoagentRe
244256
return {
245257
name,
246258
state: isInternalStateManagementWithInitial(options) ? options.initialState : {},
259+
configurable: options.configurable ?? {},
247260
running: false,
248261
active: false,
249262
threadId: undefined,

CopilotKit/packages/react-core/src/types/coagent-state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export interface CoagentState {
44
running: boolean;
55
active: boolean;
66
threadId?: string;
7+
configurable?: Record<string, any>;
78
nodeName?: string;
89
runId?: string;
910
}

CopilotKit/packages/runtime/src/graphql/inputs/agent-state.input.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ export class AgentStateInput {
77

88
@Field(() => String)
99
state: string;
10+
11+
@Field(() => String, { nullable: true })
12+
configurable?: string;
1013
}

CopilotKit/packages/runtime/src/lib/runtime/remote-action-constructors.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ export function constructLGCRemoteAction({
5555
});
5656

5757
let state = {};
58+
let configurable = {};
5859
if (agentStates) {
59-
const jsonState = agentStates.find((state) => state.agentName === name)?.state;
60+
const jsonState = agentStates.find((state) => state.agentName === name);
6061
if (jsonState) {
61-
state = JSON.parse(jsonState);
62+
state = JSON.parse(jsonState.state);
63+
configurable = JSON.parse(jsonState.configurable);
6264
}
6365
}
6466

@@ -72,6 +74,7 @@ export function constructLGCRemoteAction({
7274
nodeName,
7375
messages: [...messages, ...additionalMessages],
7476
state,
77+
configurable,
7578
properties: graphqlContext.properties,
7679
actions: actionInputsWithoutAgents.map((action) => ({
7780
name: action.name,
@@ -196,10 +199,12 @@ export function constructRemoteActions({
196199
});
197200

198201
let state = {};
202+
let configurable = {};
199203
if (agentStates) {
200-
const jsonState = agentStates.find((state) => state.agentName === name)?.state;
204+
const jsonState = agentStates.find((state) => state.agentName === name);
201205
if (jsonState) {
202-
state = JSON.parse(jsonState);
206+
state = JSON.parse(jsonState.state);
207+
configurable = JSON.parse(jsonState.configurable);
203208
}
204209
}
205210

@@ -214,6 +219,7 @@ export function constructRemoteActions({
214219
nodeName,
215220
messages: [...messages, ...additionalMessages],
216221
state,
222+
configurable,
217223
properties: graphqlContext.properties,
218224
actions: actionInputsWithoutAgents.map((action) => ({
219225
name: action.name,

CopilotKit/packages/runtime/src/lib/runtime/remote-lg-action.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface ExecutionArgs extends Omit<LangGraphPlatformEndpoint, "agents"> {
2424
nodeName: string;
2525
messages: Message[];
2626
state: State;
27+
configurable?: Record<string, any>;
2728
properties: CopilotRequestContextProperties;
2829
actions: ExecutionAction[];
2930
logger: Logger;
@@ -87,6 +88,7 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
8788
agent,
8889
nodeName: initialNodeName,
8990
state: initialState,
91+
configurable,
9092
messages,
9193
actions,
9294
logger,
@@ -202,6 +204,9 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
202204
}
203205
const assistantId = retrievedAssistant.assistant_id;
204206

207+
if (configurable) {
208+
await client.assistants.update(assistantId, { config: { configurable } });
209+
}
205210
const graphInfo = await client.assistants.getGraph(assistantId);
206211

207212
let streamingStateExtractor = new StreamingStateExtractor([]);

sdk-python/copilotkit/agent.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def execute( # pylint: disable=too-many-arguments
2727
self,
2828
*,
2929
state: dict,
30+
configurable: Optional[dict] = None,
3031
messages: List[Message],
3132
thread_id: Optional[str] = None,
3233
node_name: Optional[str] = None,

sdk-python/copilotkit/integrations/fastapi.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ async def handler(request: Request, sdk: CopilotKitRemoteEndpoint):
103103
if method == 'POST' and path == 'agents/execute':
104104
thread_id = body.get("threadId")
105105
node_name = body.get("nodeName")
106+
configurable = body.get("configurable")
106107

107108
name = body_get_or_raise(body, "name")
108109
state = body_get_or_raise(body, "state")
@@ -117,6 +118,7 @@ async def handler(request: Request, sdk: CopilotKitRemoteEndpoint):
117118
node_name=node_name,
118119
name=name,
119120
state=state,
121+
configurable=configurable,
120122
messages=messages,
121123
actions=actions,
122124
meta_events=meta_events,
@@ -174,6 +176,7 @@ def handle_execute_agent( # pylint: disable=too-many-arguments
174176
node_name: str,
175177
name: str,
176178
state: dict,
179+
configurable: Optional[dict] = None,
177180
messages: List[Message],
178181
actions: List[ActionDict],
179182
meta_events: Optional[List[MetaEvent]] = None,
@@ -186,6 +189,7 @@ def handle_execute_agent( # pylint: disable=too-many-arguments
186189
name=name,
187190
node_name=node_name,
188191
state=state,
192+
configurable=configurable,
189193
messages=messages,
190194
actions=actions,
191195
meta_events=meta_events,

sdk-python/copilotkit/langgraph_agent.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ def execute( # pylint: disable=too-many-arguments
217217
self,
218218
*,
219219
state: dict,
220+
configurable: Optional[dict] = None,
220221
messages: List[Message],
221222
thread_id: Optional[str] = None,
222223
node_name: Optional[str] = None,
@@ -225,6 +226,7 @@ def execute( # pylint: disable=too-many-arguments
225226
):
226227
return self._stream_events(
227228
state=state,
229+
configurable=configurable,
228230
messages=messages,
229231
actions=actions,
230232
thread_id=thread_id,
@@ -236,6 +238,7 @@ async def _stream_events( # pylint: disable=too-many-locals
236238
self,
237239
*,
238240
state: Any,
241+
configurable: Optional[dict] = None,
239242
messages: List[Message],
240243
thread_id: str,
241244
actions: Optional[List[ActionDict]] = None,
@@ -244,7 +247,7 @@ async def _stream_events( # pylint: disable=too-many-locals
244247
):
245248

246249
config = ensure_config(cast(Any, self.langgraph_config.copy()) if self.langgraph_config else {}) # pylint: disable=line-too-long
247-
config["configurable"] = config.get("configurable", {})
250+
config["configurable"] = {**config.get("configurable", {}), **(configurable or {})}
248251
config["configurable"]["thread_id"] = thread_id
249252

250253
agent_state = await self.graph.aget_state(config)

0 commit comments

Comments
 (0)