ag-ui-protocol / ag-ui-protocol/ag-ui

ag-ui-crewai: CopilotKitState does not declare `context`, so Pydantic drops the entries the endpoint just wrote

未關閉
#2,588 3 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
主要語言
Python
星號
15.9k
分支
1.4k
平均合併
1 天 17 小時
30 天內合併 PR
163

描述

### Summary

`ag_ui_crewai` writes `RunAgentInput.context` into the Flow run state, but the state base class it ships for Flow authors does not declare a `context` field. A Flow state is a Pydantic model, and Pydantic ignores unknown keys by default, so the key is discarded on validation before any `@start()` method runs.

The result is that `self.state.context` does not exist in the shape the quickstarts show, and there is no error — the field is simply never there. An agent whose answer was supposed to be grounded in page context answers from nothing instead, fluently and in the right shape.

Verified on `main` today (`ag-ui-crewai` 0.3.0).

### The two halves

`integrations/crew-ai/python/ag_ui_crewai/endpoint.py`, in `crewai_prepare_inputs`:

```python
# Thread ``input.context`` into the run so agent code and tools can read it
# from state. Serialize each entry to a plain dict so the flow
# state stays JSON-safe and tools can read ``entry["value"]`` directly.
context_list = [entry.model_dump() for entry in context] if context else []

new_state = {
...
"context": context_list,
...
}
```

`integrations/crew-ai/python/ag_ui_crewai/sdk.py:83`:

```python
class CopilotKitState(FlowState):
"""CopilotKit state"""
messages: List[Any] = Field(default_factory=list)
copilotkit: CopilotKitProperties = Field(default_factory=CopilotKitProperties)
# ... no `context` field
```

So the comment promising "agent code and tools can read it from state" is not true of the class the package ships for that purpose.

### Reproduction

```python
from ag_ui_crewai import CopilotKitState

class MyState(CopilotKitState):
pass

s = MyState(**{"messages": [], "context": [{"description": "queue", "value": "THE QUEUE"}]})
print(hasattr(s, "context")) # False
print(s.model_extra) # None
```

End to end, sending the same `RunAgentInput` to a Flow agent three times — twice with a populated `context` and once with `context: []` — produced three identical answers, none of them grounded in the supplied data. The empty-context control was indistinguishable from both populated runs, which is what makes this hard to notice: nothing fails, the model just invents.

Declaring the field on the state subclass is enough to make the value reachable, and the A/B then separates cleanly.

### Suggested fix

Declare it on `CopilotKitState`, so the existing endpoint behaviour works as its own comment describes and Flow authors need no per-project workaround:

```python
class CopilotKitState(FlowState):
messages: List[Any] = Field(default_factory=list)
copilotkit: CopilotKitProperties = Field(default_factory=CopilotKitProperties)
context: List[Any] = Field(default_factory=list)
```

This is additive. A Flow that ignores `context` is unaffected, and one that already declares the field itself keeps its own annotation.

### Related, but a separate decision

The ADK middleware has the same shape of gap for a different reason: `ag_ui_adk` stores `RunAgentInput.context` in session state under `CONTEXT_STATE_KEY` and nothing puts it in front of the model. `CONTEXT_STATE_KEY` is read in exactly one place in that package (`a2ui_tool.py`, for the A2UI catalog entry), and ADK's `inject_session_state` substitutes only explicit `{key}` placeholders, so the plain string `instruction` every quickstart shows leaves the entries unread.

That one is reachable by an integrator who knows the key, so it is a design call rather than a defect — but it is worth asking whether the adapter, which is the layer that knows the context arrived, should be the layer that surfaces it. Happy to split that into its own issue if you would rather keep this one to the CrewAI fix.

貢獻指南

開啟貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。