by-framework-langgraph 0.2.0__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.
- by_framework_langgraph-0.2.0/.gitignore +68 -0
- by_framework_langgraph-0.2.0/PKG-INFO +69 -0
- by_framework_langgraph-0.2.0/README.md +46 -0
- by_framework_langgraph-0.2.0/pyproject.toml +45 -0
- by_framework_langgraph-0.2.0/src/by_framework_langgraph/__init__.py +36 -0
- by_framework_langgraph-0.2.0/src/by_framework_langgraph/_utils.py +64 -0
- by_framework_langgraph-0.2.0/src/by_framework_langgraph/adapter.py +422 -0
- by_framework_langgraph-0.2.0/src/by_framework_langgraph/tools.py +118 -0
- by_framework_langgraph-0.2.0/src/by_framework_langgraph/worker.py +182 -0
- by_framework_langgraph-0.2.0/tests/test_adapter.py +408 -0
- by_framework_langgraph-0.2.0/tests/test_tools.py +55 -0
- by_framework_langgraph-0.2.0/tests/test_utils.py +86 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
share/python-wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
MANIFEST
|
|
24
|
+
|
|
25
|
+
# Environment
|
|
26
|
+
.env
|
|
27
|
+
.venv
|
|
28
|
+
env/
|
|
29
|
+
venv/
|
|
30
|
+
ENV/
|
|
31
|
+
env.bak/
|
|
32
|
+
venv.bak/
|
|
33
|
+
|
|
34
|
+
# Node.js
|
|
35
|
+
node_modules/
|
|
36
|
+
npm-debug.log*
|
|
37
|
+
yarn-debug.log*
|
|
38
|
+
yarn-error.log*
|
|
39
|
+
.pnpm-debug.log*
|
|
40
|
+
dist/
|
|
41
|
+
.cache/
|
|
42
|
+
.next/
|
|
43
|
+
out/
|
|
44
|
+
.next-env.d.ts
|
|
45
|
+
|
|
46
|
+
# IDEs / Editors
|
|
47
|
+
.idea/
|
|
48
|
+
.vscode/
|
|
49
|
+
*.swp
|
|
50
|
+
*.swo
|
|
51
|
+
.DS_Store
|
|
52
|
+
.DS_Store?
|
|
53
|
+
._*
|
|
54
|
+
.Spotlight-V100
|
|
55
|
+
.Trashes
|
|
56
|
+
ehthumbs.db
|
|
57
|
+
Thumbs.db
|
|
58
|
+
|
|
59
|
+
# Project Specific
|
|
60
|
+
.worktrees/
|
|
61
|
+
*.log
|
|
62
|
+
.gemini/
|
|
63
|
+
/brain/
|
|
64
|
+
.agents/
|
|
65
|
+
.agent/
|
|
66
|
+
*gateway-sdk.log*
|
|
67
|
+
.claude/settings.local.json
|
|
68
|
+
.claude/settings.json
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: by-framework-langgraph
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: LangGraph integration for by-framework
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: by-framework>=1.0.0
|
|
7
|
+
Requires-Dist: langchain-core>=1.2.28
|
|
8
|
+
Requires-Dist: langgraph>=1.1.6
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: isort>=5.13.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pyink>=24.0.0; extra == 'dev'
|
|
12
|
+
Requires-Dist: pylint>=3.0.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: ruff>=0.3.0; extra == 'dev'
|
|
16
|
+
Provides-Extra: langfuse
|
|
17
|
+
Requires-Dist: by-framework-trace-langfuse>=0.2.0; extra == 'langfuse'
|
|
18
|
+
Provides-Extra: phoenix
|
|
19
|
+
Requires-Dist: by-framework-trace-phoenix>=0.2.0; extra == 'phoenix'
|
|
20
|
+
Requires-Dist: openinference-instrumentation-langchain>=0.1.0; extra == 'phoenix'
|
|
21
|
+
Requires-Dist: openinference-instrumentation-openai>=0.1.0; extra == 'phoenix'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# by-framework-langgraph
|
|
25
|
+
|
|
26
|
+
LangGraph integration for by-framework. Provides two integration modes:
|
|
27
|
+
|
|
28
|
+
1. **Adapter Mode** — Plug existing LangGraph graphs into by-framework with one line
|
|
29
|
+
2. **Native Mode** — Build LangGraph workers with native `call_agent` / `ask_user` / `resume` support
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv add by-framework-langgraph
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### Adapter Mode — Plug in existing graphs
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from by_framework.worker import ByaiWorker
|
|
43
|
+
from by_framework_langgraph import LangGraphAdapter
|
|
44
|
+
|
|
45
|
+
class MyWorker(ByaiWorker):
|
|
46
|
+
def get_agent_types(self):
|
|
47
|
+
return ["my-agent"]
|
|
48
|
+
|
|
49
|
+
async def process_command(self, command, context):
|
|
50
|
+
graph = build_my_existing_graph() # your existing LangGraph
|
|
51
|
+
adapter = LangGraphAdapter(graph, context)
|
|
52
|
+
return await adapter.run(command)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Native Mode — Framework-native LangGraph workers
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from by_framework_langgraph import LangGraphWorker, make_remote_agent_tool, make_ask_user_tool
|
|
59
|
+
|
|
60
|
+
class OrchestratorWorker(LangGraphWorker):
|
|
61
|
+
def get_agent_types(self):
|
|
62
|
+
return ["orchestrator"]
|
|
63
|
+
|
|
64
|
+
def build_graph(self, context, command):
|
|
65
|
+
poet = make_remote_agent_tool(context, "invoke_poet", "poet-agent", "调度诗人创作")
|
|
66
|
+
ask = make_ask_user_tool(context)
|
|
67
|
+
llm = ChatOpenAI(model="gpt-4o").bind_tools([poet, ask])
|
|
68
|
+
# ... build and return compiled graph
|
|
69
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# by-framework-langgraph
|
|
2
|
+
|
|
3
|
+
LangGraph integration for by-framework. Provides two integration modes:
|
|
4
|
+
|
|
5
|
+
1. **Adapter Mode** — Plug existing LangGraph graphs into by-framework with one line
|
|
6
|
+
2. **Native Mode** — Build LangGraph workers with native `call_agent` / `ask_user` / `resume` support
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
uv add by-framework-langgraph
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
### Adapter Mode — Plug in existing graphs
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from by_framework.worker import ByaiWorker
|
|
20
|
+
from by_framework_langgraph import LangGraphAdapter
|
|
21
|
+
|
|
22
|
+
class MyWorker(ByaiWorker):
|
|
23
|
+
def get_agent_types(self):
|
|
24
|
+
return ["my-agent"]
|
|
25
|
+
|
|
26
|
+
async def process_command(self, command, context):
|
|
27
|
+
graph = build_my_existing_graph() # your existing LangGraph
|
|
28
|
+
adapter = LangGraphAdapter(graph, context)
|
|
29
|
+
return await adapter.run(command)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Native Mode — Framework-native LangGraph workers
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from by_framework_langgraph import LangGraphWorker, make_remote_agent_tool, make_ask_user_tool
|
|
36
|
+
|
|
37
|
+
class OrchestratorWorker(LangGraphWorker):
|
|
38
|
+
def get_agent_types(self):
|
|
39
|
+
return ["orchestrator"]
|
|
40
|
+
|
|
41
|
+
def build_graph(self, context, command):
|
|
42
|
+
poet = make_remote_agent_tool(context, "invoke_poet", "poet-agent", "调度诗人创作")
|
|
43
|
+
ask = make_ask_user_tool(context)
|
|
44
|
+
llm = ChatOpenAI(model="gpt-4o").bind_tools([poet, ask])
|
|
45
|
+
# ... build and return compiled graph
|
|
46
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "by-framework-langgraph"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "LangGraph integration for by-framework"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"by-framework>=1.0.0",
|
|
9
|
+
"langgraph>=1.1.6",
|
|
10
|
+
"langchain-core>=1.2.28",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.optional-dependencies]
|
|
14
|
+
dev = [
|
|
15
|
+
"pytest>=8.0.0",
|
|
16
|
+
"pytest-asyncio>=0.23.0",
|
|
17
|
+
"isort>=5.13.0",
|
|
18
|
+
"pyink>=24.0.0",
|
|
19
|
+
"pylint>=3.0.0",
|
|
20
|
+
"ruff>=0.3.0",
|
|
21
|
+
]
|
|
22
|
+
langfuse = [
|
|
23
|
+
"by-framework-trace-langfuse>=0.2.0",
|
|
24
|
+
]
|
|
25
|
+
phoenix = [
|
|
26
|
+
"by-framework-trace-phoenix>=0.2.0",
|
|
27
|
+
"openinference-instrumentation-langchain>=0.1.0",
|
|
28
|
+
"openinference-instrumentation-openai>=0.1.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.uv.sources]
|
|
32
|
+
by-framework = { workspace = true }
|
|
33
|
+
by-framework-trace-langfuse = { workspace = true }
|
|
34
|
+
by-framework-trace-phoenix = { workspace = true }
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["hatchling"]
|
|
38
|
+
build-backend = "hatchling.build"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/by_framework_langgraph"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
pythonpath = ["src", "../../src"]
|
|
45
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""LangGraph integration for by-framework.
|
|
2
|
+
|
|
3
|
+
Provides two integration modes:
|
|
4
|
+
|
|
5
|
+
1. **Adapter Mode** — Plug existing LangGraph graphs into by-framework::
|
|
6
|
+
|
|
7
|
+
from by_framework_langgraph import LangGraphAdapter
|
|
8
|
+
|
|
9
|
+
adapter = LangGraphAdapter(my_graph, context)
|
|
10
|
+
return await adapter.run(command)
|
|
11
|
+
|
|
12
|
+
2. **Native Mode** — Build LangGraph workers with framework-native tools::
|
|
13
|
+
|
|
14
|
+
from by_framework_langgraph import (
|
|
15
|
+
LangGraphWorker,
|
|
16
|
+
make_remote_agent_tool,
|
|
17
|
+
make_ask_user_tool,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
class MyWorker(LangGraphWorker):
|
|
21
|
+
def build_graph(self, context, command):
|
|
22
|
+
poet = make_remote_agent_tool(context, ...)
|
|
23
|
+
ask = make_ask_user_tool(context)
|
|
24
|
+
# ... build and return compiled graph
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from .adapter import LangGraphAdapter
|
|
28
|
+
from .tools import make_ask_user_tool, make_remote_agent_tool
|
|
29
|
+
from .worker import LangGraphWorker
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"LangGraphAdapter",
|
|
33
|
+
"LangGraphWorker",
|
|
34
|
+
"make_ask_user_tool",
|
|
35
|
+
"make_remote_agent_tool",
|
|
36
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Internal utilities for LangGraph integration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from by_framework.core.protocol.commands import ResumeCommand
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def extract_content_text(content: Any) -> str:
|
|
12
|
+
"""Extract plain text from various command content formats.
|
|
13
|
+
|
|
14
|
+
Handles:
|
|
15
|
+
- str → return directly
|
|
16
|
+
- list[dict] (BaiYing message format) → extract text fields
|
|
17
|
+
- other → str() conversion
|
|
18
|
+
"""
|
|
19
|
+
if isinstance(content, str):
|
|
20
|
+
return content
|
|
21
|
+
|
|
22
|
+
if isinstance(content, list):
|
|
23
|
+
texts: list[str] = []
|
|
24
|
+
for item in content:
|
|
25
|
+
if isinstance(item, dict):
|
|
26
|
+
# BaiYing message format: {"type": "text", "text": "..."}
|
|
27
|
+
text = item.get("text", "")
|
|
28
|
+
if text:
|
|
29
|
+
texts.append(str(text))
|
|
30
|
+
# Fallback: {"content": "..."}
|
|
31
|
+
elif "content" in item:
|
|
32
|
+
texts.append(str(item["content"]))
|
|
33
|
+
elif isinstance(item, str):
|
|
34
|
+
texts.append(item)
|
|
35
|
+
return "\n".join(texts) if texts else str(content)
|
|
36
|
+
|
|
37
|
+
return str(content)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def extract_resume_data(command: ResumeCommand) -> str:
|
|
41
|
+
"""Extract resume data from a ResumeCommand.
|
|
42
|
+
|
|
43
|
+
Prioritizes reply_data (from call_agent callback) over content
|
|
44
|
+
(from ask_user reply), converting to string.
|
|
45
|
+
"""
|
|
46
|
+
if command.reply_data is not None:
|
|
47
|
+
if isinstance(command.reply_data, str):
|
|
48
|
+
return command.reply_data
|
|
49
|
+
return str(command.reply_data)
|
|
50
|
+
|
|
51
|
+
if command.content:
|
|
52
|
+
return extract_content_text(command.content)
|
|
53
|
+
|
|
54
|
+
return ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def str_to_uint128(s: str) -> int:
|
|
58
|
+
"""Convert a string to a 128-bit integer (for OTEL TraceId)."""
|
|
59
|
+
return int(hashlib.md5(s.encode()).hexdigest(), 16)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def str_to_uint64(s: str) -> int:
|
|
63
|
+
"""Convert a string to a 64-bit integer (for OTEL SpanId)."""
|
|
64
|
+
return int(hashlib.md5(s.encode()).hexdigest()[:16], 16)
|