langgraph-stream-parser 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/PKG-INFO +22 -1
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/README.md +21 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/pyproject.toml +1 -1
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/demo/__init__.py +7 -1
- langgraph_stream_parser-0.2.2/src/langgraph_stream_parser/demo/stub.py +139 -0
- langgraph_stream_parser-0.2.2/tests/test_demo_stub.py +65 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/.github/workflows/ci.yml +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/.github/workflows/release.yml +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/.gitignore +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/CHANGELOG.md +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/LICENSE +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/assets/header.svg +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/examples/agent.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/examples/fastapi_websocket.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/examples/jupyter_example.ipynb +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/spec.md +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/base.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/cli.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/fastapi.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/jupyter.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/print.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/adapters/session.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/compat.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/demo/agent.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/events.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/extractors/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/extractors/base.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/extractors/builtins.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/extractors/interrupts.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/extractors/messages.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/handlers/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/handlers/messages.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/handlers/updates.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/host/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/host/__main__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/host/config.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/host/loader.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/host/workspace.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/parser.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/src/langgraph_stream_parser/resume.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/fixtures/__init__.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/fixtures/mocks.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_cli_adapter.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_compat.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_demo.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_dual_mode.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_events.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_extractors.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_fastapi_adapter.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_generic_extractor.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_host.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_host_config.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_jupyter.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_lc14_compat.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_parser.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_print_adapter.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_real_model.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_reasoning_display.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_resume.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_session_adapter.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_subagent.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_v2_stream.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_wire_contract.py +0 -0
- {langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-stream-parser
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Universal parser for LangGraph streaming outputs
|
|
5
5
|
Project-URL: Homepage, https://github.com/dkedar7/langgraph-stream-parser
|
|
6
6
|
Project-URL: Documentation, https://github.com/dkedar7/langgraph-stream-parser#readme
|
|
@@ -49,6 +49,27 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
|
|
50
50
|
Universal parser for LangGraph streaming outputs. Normalizes complex, variable output shapes from `graph.stream()` and `graph.astream()` into consistent, typed event objects.
|
|
51
51
|
|
|
52
|
+
## One agent, every surface
|
|
53
|
+
|
|
54
|
+
`langgraph-stream-parser` is the shared core of the **deep-agent family**: write your agent once — any LangGraph `CompiledGraph` — and run it on every surface with the same spec string (`module:attr` or `path/to/file.py:attr`), the same `deepagents.toml` config file, and the same `DEEPAGENT_*` environment variables.
|
|
55
|
+
|
|
56
|
+
| Surface | Package | Try it |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| Web app | [cowork-dash](https://github.com/dkedar7/cowork-dash) | `cowork-dash run --agent my_agent.py:graph` |
|
|
59
|
+
| JupyterLab | [deepagent-lab](https://github.com/dkedar7/deepagent-lab) | `pip install deepagent-lab`, then the chat sidebar in `jupyter lab` |
|
|
60
|
+
| Terminal | [deepagent-code](https://github.com/dkedar7/deepagent-code) | `deepagent-code -a my_agent.py:graph` |
|
|
61
|
+
| VS Code | [deepagent-vscode](https://github.com/dkedar7/deepagent-vscode) | chat participant + stdio sidecar |
|
|
62
|
+
| Reference agent | [deepagent-hermes](https://github.com/dkedar7/deepagent-hermes) | `DEEPAGENT_AGENT_SPEC=deepagent_hermes.agent:graph` on any surface |
|
|
63
|
+
| Shared core | langgraph-stream-parser | **you are here** |
|
|
64
|
+
|
|
65
|
+
No agent yet? Every surface has a keyless demo mode backed by this package's stub agent — no API key required:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export DEEPAGENT_AGENT_SPEC=langgraph_stream_parser.demo.stub:graph # or each CLI's --demo flag
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
And the resolved configuration (each value, its source, and the env var / `deepagents.toml` key that sets it) is printable everywhere: `python -m langgraph_stream_parser.host`, or each CLI's `--show-config`.
|
|
72
|
+
|
|
52
73
|
## Installation
|
|
53
74
|
|
|
54
75
|
```bash
|
|
@@ -6,6 +6,27 @@
|
|
|
6
6
|
|
|
7
7
|
Universal parser for LangGraph streaming outputs. Normalizes complex, variable output shapes from `graph.stream()` and `graph.astream()` into consistent, typed event objects.
|
|
8
8
|
|
|
9
|
+
## One agent, every surface
|
|
10
|
+
|
|
11
|
+
`langgraph-stream-parser` is the shared core of the **deep-agent family**: write your agent once — any LangGraph `CompiledGraph` — and run it on every surface with the same spec string (`module:attr` or `path/to/file.py:attr`), the same `deepagents.toml` config file, and the same `DEEPAGENT_*` environment variables.
|
|
12
|
+
|
|
13
|
+
| Surface | Package | Try it |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Web app | [cowork-dash](https://github.com/dkedar7/cowork-dash) | `cowork-dash run --agent my_agent.py:graph` |
|
|
16
|
+
| JupyterLab | [deepagent-lab](https://github.com/dkedar7/deepagent-lab) | `pip install deepagent-lab`, then the chat sidebar in `jupyter lab` |
|
|
17
|
+
| Terminal | [deepagent-code](https://github.com/dkedar7/deepagent-code) | `deepagent-code -a my_agent.py:graph` |
|
|
18
|
+
| VS Code | [deepagent-vscode](https://github.com/dkedar7/deepagent-vscode) | chat participant + stdio sidecar |
|
|
19
|
+
| Reference agent | [deepagent-hermes](https://github.com/dkedar7/deepagent-hermes) | `DEEPAGENT_AGENT_SPEC=deepagent_hermes.agent:graph` on any surface |
|
|
20
|
+
| Shared core | langgraph-stream-parser | **you are here** |
|
|
21
|
+
|
|
22
|
+
No agent yet? Every surface has a keyless demo mode backed by this package's stub agent — no API key required:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export DEEPAGENT_AGENT_SPEC=langgraph_stream_parser.demo.stub:graph # or each CLI's --demo flag
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
And the resolved configuration (each value, its source, and the env var / `deepagents.toml` key that sets it) is printable everywhere: `python -m langgraph_stream_parser.host`, or each CLI's `--show-config`.
|
|
29
|
+
|
|
9
30
|
## Installation
|
|
10
31
|
|
|
11
32
|
```bash
|
|
@@ -9,7 +9,13 @@ everything.
|
|
|
9
9
|
Requires the optional ``deepagents`` dependency:
|
|
10
10
|
|
|
11
11
|
pip install langgraph-stream-parser[demo]
|
|
12
|
+
|
|
13
|
+
This package also ships the keyless **stub agent** behind every surface's
|
|
14
|
+
``--demo`` mode (:func:`create_stub_agent` / spec
|
|
15
|
+
``langgraph_stream_parser.demo.stub:graph``) — that one needs no extra and no
|
|
16
|
+
API key.
|
|
12
17
|
"""
|
|
13
18
|
from .agent import create_default_agent
|
|
19
|
+
from .stub import create_stub_agent
|
|
14
20
|
|
|
15
|
-
__all__ = ["create_default_agent"]
|
|
21
|
+
__all__ = ["create_default_agent", "create_stub_agent"]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""A keyless, deterministic stub agent — the engine behind every ``--demo`` mode.
|
|
2
|
+
|
|
3
|
+
Every deep-agent surface (cowork-dash, deepagent-code, deepagent-lab,
|
|
4
|
+
deepagent-vscode) offers a demo mode so a new user can see the surface working
|
|
5
|
+
before configuring a real agent or any API key. This module is the single
|
|
6
|
+
shared implementation: a real compiled LangGraph graph with a checkpointer,
|
|
7
|
+
streaming token-by-token through the exact same parser path as a production
|
|
8
|
+
agent — but the "model" is a local echo, so it needs no network and no keys.
|
|
9
|
+
|
|
10
|
+
Point any surface at it with the standard spec string::
|
|
11
|
+
|
|
12
|
+
DEEPAGENT_AGENT_SPEC=langgraph_stream_parser.demo.stub:graph
|
|
13
|
+
|
|
14
|
+
or build a customized one in code::
|
|
15
|
+
|
|
16
|
+
from langgraph_stream_parser.demo import create_stub_agent
|
|
17
|
+
agent = create_stub_agent(name="My Demo")
|
|
18
|
+
|
|
19
|
+
``langgraph`` and ``langchain-core`` are imported lazily — importing this
|
|
20
|
+
module stays dependency-free; only building the agent requires them (every
|
|
21
|
+
host surface already depends on both).
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
DEFAULT_REPLY_PREFIX = "(demo agent) You said: "
|
|
28
|
+
DEFAULT_NAME = "Demo Agent"
|
|
29
|
+
|
|
30
|
+
_GRAPH_CACHE: Any = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_stub_agent(
|
|
34
|
+
*,
|
|
35
|
+
name: str = DEFAULT_NAME,
|
|
36
|
+
reply_prefix: str = DEFAULT_REPLY_PREFIX,
|
|
37
|
+
checkpointer: Any = None,
|
|
38
|
+
):
|
|
39
|
+
"""Build the echo stub agent.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
name: Display name surfaced in host UIs.
|
|
43
|
+
reply_prefix: Prepended to the echoed user message in every reply.
|
|
44
|
+
checkpointer: LangGraph checkpointer. Defaults to an in-memory saver
|
|
45
|
+
so multi-turn threads work out of the box.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A compiled LangGraph graph that replies to each user message with
|
|
49
|
+
``reply_prefix + <last human message>``, streamed token-by-token.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
RuntimeError: If ``langgraph`` / ``langchain-core`` are not installed.
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
from langchain_core.callbacks import CallbackManagerForLLMRun
|
|
56
|
+
from langchain_core.language_models import BaseChatModel
|
|
57
|
+
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage
|
|
58
|
+
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
|
59
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
60
|
+
from langgraph.graph import END, START, MessagesState, StateGraph
|
|
61
|
+
except ImportError as e: # pragma: no cover — exercised only without the deps
|
|
62
|
+
raise RuntimeError(
|
|
63
|
+
"The stub agent needs langgraph + langchain-core "
|
|
64
|
+
"(every deep-agent surface already installs them): "
|
|
65
|
+
f"{e}"
|
|
66
|
+
) from e
|
|
67
|
+
|
|
68
|
+
from typing import Iterator, List, Optional
|
|
69
|
+
|
|
70
|
+
def _last_human(messages: List[BaseMessage]) -> str:
|
|
71
|
+
for message in reversed(messages):
|
|
72
|
+
if getattr(message, "type", None) == "human":
|
|
73
|
+
content = message.content
|
|
74
|
+
return content if isinstance(content, str) else str(content)
|
|
75
|
+
return ""
|
|
76
|
+
|
|
77
|
+
class EchoChatModel(BaseChatModel):
|
|
78
|
+
"""A no-API chat model that echoes the user's last message.
|
|
79
|
+
|
|
80
|
+
Implements ``_stream`` so that under LangGraph's ``messages`` stream
|
|
81
|
+
mode the reply is emitted token-by-token, matching how a real chat
|
|
82
|
+
model behaves through the parser.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def _llm_type(self) -> str:
|
|
87
|
+
return "echo-stub"
|
|
88
|
+
|
|
89
|
+
def _generate(
|
|
90
|
+
self,
|
|
91
|
+
messages: List[BaseMessage],
|
|
92
|
+
stop: Optional[List[str]] = None,
|
|
93
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
94
|
+
**kwargs: Any,
|
|
95
|
+
) -> ChatResult:
|
|
96
|
+
text = reply_prefix + _last_human(messages)
|
|
97
|
+
return ChatResult(generations=[ChatGeneration(message=AIMessage(content=text))])
|
|
98
|
+
|
|
99
|
+
def _stream(
|
|
100
|
+
self,
|
|
101
|
+
messages: List[BaseMessage],
|
|
102
|
+
stop: Optional[List[str]] = None,
|
|
103
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
104
|
+
**kwargs: Any,
|
|
105
|
+
) -> Iterator[ChatGenerationChunk]:
|
|
106
|
+
text = reply_prefix + _last_human(messages)
|
|
107
|
+
tokens = text.split(" ")
|
|
108
|
+
for i, token in enumerate(tokens):
|
|
109
|
+
piece = token if i == len(tokens) - 1 else token + " "
|
|
110
|
+
chunk = ChatGenerationChunk(message=AIMessageChunk(content=piece))
|
|
111
|
+
if run_manager is not None:
|
|
112
|
+
run_manager.on_llm_new_token(piece, chunk=chunk)
|
|
113
|
+
yield chunk
|
|
114
|
+
|
|
115
|
+
model = EchoChatModel()
|
|
116
|
+
|
|
117
|
+
def _respond(state: MessagesState) -> dict:
|
|
118
|
+
return {"messages": [model.invoke(state["messages"])]}
|
|
119
|
+
|
|
120
|
+
builder = StateGraph(MessagesState)
|
|
121
|
+
builder.add_node("respond", _respond)
|
|
122
|
+
builder.add_edge(START, "respond")
|
|
123
|
+
builder.add_edge("respond", END)
|
|
124
|
+
|
|
125
|
+
graph = builder.compile(checkpointer=checkpointer or MemorySaver())
|
|
126
|
+
graph.name = name
|
|
127
|
+
return graph
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def __getattr__(attr: str) -> Any:
|
|
131
|
+
# ``graph`` is built lazily on first access so plain imports of this module
|
|
132
|
+
# never require langgraph — but ``load_agent_spec("...demo.stub:graph")``
|
|
133
|
+
# gets a ready compiled agent.
|
|
134
|
+
if attr == "graph":
|
|
135
|
+
global _GRAPH_CACHE
|
|
136
|
+
if _GRAPH_CACHE is None:
|
|
137
|
+
_GRAPH_CACHE = create_stub_agent()
|
|
138
|
+
return _GRAPH_CACHE
|
|
139
|
+
raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Tests for the keyless stub agent behind every surface's --demo mode.
|
|
2
|
+
|
|
3
|
+
Unlike the default agent (which needs deepagents + an API key), the stub only
|
|
4
|
+
needs langgraph + langchain-core — both in the dev extras — so these tests
|
|
5
|
+
exercise it end to end through the parser.
|
|
6
|
+
"""
|
|
7
|
+
from langgraph_stream_parser import StreamParser, load_agent_spec, prepare_agent_input
|
|
8
|
+
from langgraph_stream_parser.demo import create_stub_agent
|
|
9
|
+
from langgraph_stream_parser.events import CompleteEvent, ContentEvent
|
|
10
|
+
|
|
11
|
+
STREAM_MODE = ["updates", "messages"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _run_turn(graph, message: str, thread_id: str = "t1"):
|
|
15
|
+
parser = StreamParser(stream_mode=STREAM_MODE)
|
|
16
|
+
stream = graph.stream(
|
|
17
|
+
prepare_agent_input(message=message),
|
|
18
|
+
config={"configurable": {"thread_id": thread_id}},
|
|
19
|
+
stream_mode=STREAM_MODE,
|
|
20
|
+
)
|
|
21
|
+
return list(parser.parse(stream))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _content(events) -> str:
|
|
25
|
+
return "".join(e.content for e in events if isinstance(e, ContentEvent))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_streams_echo_through_the_parser():
|
|
29
|
+
graph = create_stub_agent()
|
|
30
|
+
events = _run_turn(graph, "hello demo")
|
|
31
|
+
|
|
32
|
+
assert "(demo agent) You said: hello demo" in _content(events)
|
|
33
|
+
assert isinstance(events[-1], CompleteEvent)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_streams_token_by_token():
|
|
37
|
+
graph = create_stub_agent()
|
|
38
|
+
events = _run_turn(graph, "one two three four")
|
|
39
|
+
content_events = [e for e in events if isinstance(e, ContentEvent)]
|
|
40
|
+
# The echo splits on spaces — a multi-word message must arrive in pieces.
|
|
41
|
+
assert len(content_events) > 1
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_multi_turn_thread_persists():
|
|
45
|
+
graph = create_stub_agent()
|
|
46
|
+
_run_turn(graph, "first", thread_id="conv")
|
|
47
|
+
state = graph.get_state({"configurable": {"thread_id": "conv"}})
|
|
48
|
+
_run_turn(graph, "second", thread_id="conv")
|
|
49
|
+
state2 = graph.get_state({"configurable": {"thread_id": "conv"}})
|
|
50
|
+
assert len(state2.values["messages"]) > len(state.values["messages"])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_custom_name_and_prefix():
|
|
54
|
+
graph = create_stub_agent(name="My Demo", reply_prefix="echo: ")
|
|
55
|
+
assert graph.name == "My Demo"
|
|
56
|
+
events = _run_turn(graph, "hi")
|
|
57
|
+
assert "echo: hi" in _content(events)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_loadable_via_standard_spec_string():
|
|
61
|
+
graph = load_agent_spec("langgraph_stream_parser.demo.stub:graph")
|
|
62
|
+
events = _run_turn(graph, "spec works", thread_id="spec")
|
|
63
|
+
assert "spec works" in _content(events)
|
|
64
|
+
# The module-level graph is cached — same object on a second load.
|
|
65
|
+
assert load_agent_spec("langgraph_stream_parser.demo.stub:graph") is graph
|
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/.github/workflows/release.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/examples/fastapi_websocket.py
RENAMED
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/examples/jupyter_example.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_fastapi_adapter.py
RENAMED
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_generic_extractor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_reasoning_display.py
RENAMED
|
File without changes
|
|
File without changes
|
{langgraph_stream_parser-0.2.1 → langgraph_stream_parser-0.2.2}/tests/test_session_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|