agentship-langgraph 0.0.1__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.
- agentship_langgraph-0.0.1/.gitignore +27 -0
- agentship_langgraph-0.0.1/PKG-INFO +24 -0
- agentship_langgraph-0.0.1/README.md +3 -0
- agentship_langgraph-0.0.1/pyproject.toml +42 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/__init__.py +17 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/agent.py +77 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/durability.py +58 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/engine.py +725 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/mcp.py +159 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/mcp_auth.py +93 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/models.py +201 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/py.typed +0 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/studio.py +33 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/templates/__init__.py +68 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/templates/autonomous_tpl.py +86 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/templates/graph.py +100 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/templates/graph_config.py +76 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/templates/graph_supervisor.py +377 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/templates/single.py +40 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/testing.py +130 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/tools.py +185 -0
- agentship_langgraph-0.0.1/src/agentship_langgraph/tracing.py +414 -0
- agentship_langgraph-0.0.1/tests/cassettes/test_assistant_example/test_assistant_example_runs_live_via_cassette.yaml +137 -0
- agentship_langgraph-0.0.1/tests/cassettes/test_live_model/test_real_gpt_4o_mini_returns_a_non_empty_answer.yaml +137 -0
- agentship_langgraph-0.0.1/tests/cassettes/test_providers/openai.yaml +120 -0
- agentship_langgraph-0.0.1/tests/cassettes/test_streaming_live/test_real_gpt_4o_mini_streams_multiple_token_chunks.yaml +176 -0
- agentship_langgraph-0.0.1/tests/fixtures/mcp_echo_server.py +22 -0
- agentship_langgraph-0.0.1/tests/providers.py +54 -0
- agentship_langgraph-0.0.1/tests/test_assistant_example.py +52 -0
- agentship_langgraph-0.0.1/tests/test_authoring_examples.py +84 -0
- agentship_langgraph-0.0.1/tests/test_autonomous_template.py +100 -0
- agentship_langgraph-0.0.1/tests/test_custom_build_graph.py +119 -0
- agentship_langgraph-0.0.1/tests/test_declarative_supervisor.py +61 -0
- agentship_langgraph-0.0.1/tests/test_durability.py +57 -0
- agentship_langgraph-0.0.1/tests/test_graph_config.py +81 -0
- agentship_langgraph-0.0.1/tests/test_graph_supervisor.py +177 -0
- agentship_langgraph-0.0.1/tests/test_graph_template.py +79 -0
- agentship_langgraph-0.0.1/tests/test_langgraph_durable.py +153 -0
- agentship_langgraph-0.0.1/tests/test_langgraph_engine.py +284 -0
- agentship_langgraph-0.0.1/tests/test_langgraph_hitl.py +115 -0
- agentship_langgraph-0.0.1/tests/test_langgraph_registration.py +25 -0
- agentship_langgraph-0.0.1/tests/test_live_model.py +59 -0
- agentship_langgraph-0.0.1/tests/test_mcp.py +107 -0
- agentship_langgraph-0.0.1/tests/test_mcp_auth.py +53 -0
- agentship_langgraph-0.0.1/tests/test_mcp_discovery_timeout.py +73 -0
- agentship_langgraph-0.0.1/tests/test_mcp_tool_errors.py +110 -0
- agentship_langgraph-0.0.1/tests/test_mixed_engine_dispatch.py +52 -0
- agentship_langgraph-0.0.1/tests/test_models.py +75 -0
- agentship_langgraph-0.0.1/tests/test_otlp_wire_export.py +246 -0
- agentship_langgraph-0.0.1/tests/test_provider_examples.py +124 -0
- agentship_langgraph-0.0.1/tests/test_providers.py +102 -0
- agentship_langgraph-0.0.1/tests/test_resume_gate.py +48 -0
- agentship_langgraph-0.0.1/tests/test_router_purity.py +97 -0
- agentship_langgraph-0.0.1/tests/test_session_memory.py +75 -0
- agentship_langgraph-0.0.1/tests/test_single_template.py +149 -0
- agentship_langgraph-0.0.1/tests/test_stream_tool_events.py +75 -0
- agentship_langgraph-0.0.1/tests/test_streaming_live.py +87 -0
- agentship_langgraph-0.0.1/tests/test_streaming_sends_tokens_as_they_arrive.py +127 -0
- agentship_langgraph-0.0.1/tests/test_subagent_trace_shape.py +129 -0
- agentship_langgraph-0.0.1/tests/test_supervisor_stream_content.py +61 -0
- agentship_langgraph-0.0.1/tests/test_tool_execution.py +302 -0
- agentship_langgraph-0.0.1/tests/test_tracing_callback.py +147 -0
- agentship_langgraph-0.0.1/tests/test_tracing_callback_parity.py +137 -0
- agentship_langgraph-0.0.1/tests/test_tracing_engine_integration.py +174 -0
- agentship_langgraph-0.0.1/tests/test_tuning_examples.py +68 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
|
|
16
|
+
# Env / secrets — never commit
|
|
17
|
+
.env
|
|
18
|
+
.env.*
|
|
19
|
+
!.env.example
|
|
20
|
+
|
|
21
|
+
# Editor / OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
|
|
26
|
+
# Docs build
|
|
27
|
+
site/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: agentship-langgraph
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: AgentShip LangGraph engine — the default real-model runtime, with a LiteLLM model source that routes one `model:` string to any provider.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Requires-Dist: agentship-core==0.0.1
|
|
8
|
+
Requires-Dist: langchain-core>=0.3
|
|
9
|
+
Requires-Dist: langchain-litellm>=0.1
|
|
10
|
+
Requires-Dist: langgraph>=0.2
|
|
11
|
+
Provides-Extra: autonomous
|
|
12
|
+
Requires-Dist: deepagents==0.6.12; extra == 'autonomous'
|
|
13
|
+
Provides-Extra: mcp
|
|
14
|
+
Requires-Dist: cryptography>=42; extra == 'mcp'
|
|
15
|
+
Requires-Dist: langchain-mcp-adapters>=0.3; extra == 'mcp'
|
|
16
|
+
Requires-Dist: mcp<2,>=1.28; extra == 'mcp'
|
|
17
|
+
Provides-Extra: postgres
|
|
18
|
+
Requires-Dist: langgraph-checkpoint-postgres>=2.0; extra == 'postgres'
|
|
19
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# agentship-langgraph
|
|
23
|
+
|
|
24
|
+
The default real-model engine for AgentShip: a single-agent LangGraph graph over a LiteLLM-backed model. Registers the `langgraph` engine via the `agentship.engines` entry point. Import as `agentship_langgraph`.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agentship-langgraph"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "AgentShip LangGraph engine — the default real-model runtime, with a LiteLLM model source that routes one `model:` string to any provider."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"agentship-core==0.0.1",
|
|
14
|
+
"langgraph>=0.2",
|
|
15
|
+
"langchain-core>=0.3",
|
|
16
|
+
"langchain-litellm>=0.1", # the default model source: one `model:` string routes to any provider
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# The `autonomous` template is opt-in: `pip install agentship-langgraph[autonomous]`.
|
|
20
|
+
# It wraps the `deepagents` library, pinned to 0.6.12 (pre-1.0) because
|
|
21
|
+
# create_deep_agent's signature can drift; the template imports it lazily, so the base
|
|
22
|
+
# engine installs and runs without it, and `agentship doctor` flags a version mismatch
|
|
23
|
+
# (see templates/autonomous_tpl.py).
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
autonomous = ["deepagents==0.6.12"]
|
|
26
|
+
# MCP tools (Phase 03 §C3): consume the OSS client stack — do NOT hand-roll it.
|
|
27
|
+
# `langchain-mcp-adapters` (MultiServerMCPClient) connects to local stdio + remote streamable-HTTP
|
|
28
|
+
# MCP servers and returns LangChain tools the engine binds. `mcp` is pinned <2 because v2 reshaped
|
|
29
|
+
# the client API the adapters target. Opt-in: a bare install runs without MCP; imported lazily.
|
|
30
|
+
mcp = ["langchain-mcp-adapters>=0.3", "mcp>=1.28,<2", "cryptography>=42"]
|
|
31
|
+
# Durable checkpointing (Phase 02 §C4): the AsyncPostgresSaver + its psycopg driver.
|
|
32
|
+
# Opt-in so a bare install runs on the in-memory saver; `pip install agentship-langgraph[postgres]`
|
|
33
|
+
# turns on crash-durable, resumable runs. Pairs with agentship-core[postgres] (the ThreadLock).
|
|
34
|
+
postgres = ["langgraph-checkpoint-postgres>=2.0", "psycopg[binary]>=3.1"]
|
|
35
|
+
|
|
36
|
+
# Registers the LangGraph engine with the core harness (discovered via importlib.metadata),
|
|
37
|
+
# so `engine: langgraph` in a spec resolves here.
|
|
38
|
+
[project.entry-points."agentship.engines"]
|
|
39
|
+
langgraph = "agentship_langgraph.engine:LangGraphEngine"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/agentship_langgraph"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""The LangGraph engine package — AgentShip's default real-model engine.
|
|
2
|
+
|
|
3
|
+
:class:`~agentship_langgraph.engine.LangGraphEngine` builds the agent — either its
|
|
4
|
+
own default single-agent graph (system prompt + user input → chat model → answer)
|
|
5
|
+
or, when a developer subclasses :class:`~agentship_langgraph.agent.LangGraphAgent`
|
|
6
|
+
and implements ``build_graph(model, tools)``, that author's native graph — over a
|
|
7
|
+
LiteLLM-backed model resolved by :mod:`agentship_langgraph.models`. It ships as the
|
|
8
|
+
``agentship-langgraph`` dist and is discovered via the ``agentship.engines``
|
|
9
|
+
entry-point group, so ``engine: langgraph`` in a spec resolves here.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from .agent import LangGraphAgent
|
|
15
|
+
from .engine import LangGraphEngine
|
|
16
|
+
|
|
17
|
+
__all__ = ["LangGraphAgent", "LangGraphEngine"]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""The custom-authoring base class: :class:`LangGraphAgent`.
|
|
2
|
+
|
|
3
|
+
This is the answer to CRUX 1 (DESIGN §4.3): *"how do I build a custom LangGraph
|
|
4
|
+
agent while still getting AgentShip's wired services?"* A developer subclasses
|
|
5
|
+
:class:`LangGraphAgent` and writes native LangGraph in :meth:`build_graph` — nodes,
|
|
6
|
+
edges, conditional routing, subgraphs, anything LangGraph does. The engine's
|
|
7
|
+
``build(spec)`` resolves the ``model`` and ``tools`` and hands them to
|
|
8
|
+
``build_graph`` as plain, already-wired params; the author never wires a vendor
|
|
9
|
+
themselves and never loses native power. Per-request services (memory recall,
|
|
10
|
+
tracing, caller identity) are read from ``RunContext`` inside the nodes, not passed
|
|
11
|
+
in a bundle — there is deliberately no ``EngineKit``.
|
|
12
|
+
|
|
13
|
+
The author points a spec's ``code:`` at a factory that returns a configured
|
|
14
|
+
subclass instance (which carries its own ``.spec``); the core threads that object
|
|
15
|
+
to :meth:`~agentship_langgraph.engine.LangGraphEngine.build`, which calls
|
|
16
|
+
``build_graph``. Every LangChain/LangGraph type stays inside this adapter — the
|
|
17
|
+
vendor-neutral core never sees one.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from abc import ABC, abstractmethod
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
from agentship.errors import CapabilityError
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from agentship.spec import AgentSpec
|
|
29
|
+
from langchain_core.language_models.chat_models import BaseChatModel
|
|
30
|
+
from langchain_core.tools import BaseTool
|
|
31
|
+
from langgraph.graph import StateGraph
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LangGraphAgent(ABC):
|
|
35
|
+
"""Base class a developer subclasses to author a native LangGraph agent.
|
|
36
|
+
|
|
37
|
+
A subclass implements :meth:`build_graph` in native LangGraph; the engine
|
|
38
|
+
binding is fixed to ``"langgraph"`` and ``run``/``stream``/``resume`` are
|
|
39
|
+
provided by the engine adapter (the author never writes them). Construct one
|
|
40
|
+
with its :class:`~agentship.spec.AgentSpec`; the spec's ``engine`` must be
|
|
41
|
+
``"langgraph"`` or construction fails fast with a
|
|
42
|
+
:class:`~agentship.errors.CapabilityError` (never a silent mis-bind).
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
#: The engine this agent is bound to. Fixed for every LangGraph agent so the
|
|
46
|
+
#: subclass never has to restate it; the spec's ``engine`` must still match.
|
|
47
|
+
engine_name = "langgraph"
|
|
48
|
+
|
|
49
|
+
def __init__(self, spec: AgentSpec) -> None:
|
|
50
|
+
"""Bind ``spec`` to this agent, asserting its engine is ``langgraph``.
|
|
51
|
+
|
|
52
|
+
The custom-authoring path only makes sense when the spec targets this
|
|
53
|
+
engine; a spec whose ``engine`` is anything else is a misconfiguration, so
|
|
54
|
+
it fails fast here (declare, don't fake) rather than compiling a graph the
|
|
55
|
+
wrong engine would try to drive.
|
|
56
|
+
"""
|
|
57
|
+
if spec.engine != self.engine_name:
|
|
58
|
+
raise CapabilityError(
|
|
59
|
+
f"{type(self).__name__} is a LangGraph agent, but its spec sets "
|
|
60
|
+
f"engine: {spec.engine!r} — set engine: {self.engine_name!r} or use the "
|
|
61
|
+
f"matching engine's agent base class."
|
|
62
|
+
)
|
|
63
|
+
self.spec = spec
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def build_graph(self, model: BaseChatModel, tools: list[BaseTool]) -> StateGraph:
|
|
67
|
+
"""Return a native LangGraph ``StateGraph`` built over the wired pieces.
|
|
68
|
+
|
|
69
|
+
The engine passes ``model`` (a LiteLLM-backed, cost-traced chat model it
|
|
70
|
+
resolved from the spec / ``RunContext.routed_model``) and ``tools`` (adapted
|
|
71
|
+
MCP/python tools — an empty list until Phase 03 wires tool execution). Build
|
|
72
|
+
and return your graph using them; the engine compiles it (attaching the
|
|
73
|
+
checkpointer/store it resolved, plus callbacks) and drives ``run``/``stream``.
|
|
74
|
+
You may return an already-compiled graph — the engine detects that and reuses
|
|
75
|
+
it. Read per-request services (memory, tracing, caller identity) from
|
|
76
|
+
``RunContext`` via ``get_run_context()`` inside your nodes.
|
|
77
|
+
"""
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""The durable-checkpointer substrate for the LangGraph engine (Phase 02 · C4).
|
|
2
|
+
|
|
3
|
+
One decision lives here: which LangGraph checkpointer a run uses. With no database configured
|
|
4
|
+
(dev, tests, the Week-1 slice) it is an in-memory saver; with ``AGENT_SESSION_STORE_URI`` set it
|
|
5
|
+
is a real ``AsyncPostgresSaver`` whose per-node checkpoints survive a crash so ``engine.resume``
|
|
6
|
+
can continue from the frontier. Because the Postgres saver owns a live connection pool that must
|
|
7
|
+
stay open for as long as the graph runs, this is exposed as an **async context manager**, not a
|
|
8
|
+
factory that returns a bare object.
|
|
9
|
+
|
|
10
|
+
``setup=True`` runs the saver's schema DDL. That is a migration, so per the project's "never migrate
|
|
11
|
+
silently" policy the caller opts in — it is invoked from ``agentship doctor`` / first-boot, never on
|
|
12
|
+
every process start. See phase 02 §C4 and DESIGN §6/§13.6.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import AsyncIterator
|
|
18
|
+
from contextlib import asynccontextmanager
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from langgraph.checkpoint.memory import InMemorySaver
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING: # only for typing; the Postgres saver is imported lazily so a bare install works
|
|
24
|
+
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
25
|
+
|
|
26
|
+
#: One process-wide in-memory saver for the no-database path. It must be **shared** across
|
|
27
|
+
#: ``open_checkpointer`` calls so an in-process ``run`` then ``resume`` see the same checkpoints
|
|
28
|
+
#: (a fresh saver per call would forget them). Entries are keyed by ``thread_id`` internally, so
|
|
29
|
+
#: distinct runs never collide. This is single-process only — real cross-process durability needs
|
|
30
|
+
#: the Postgres saver.
|
|
31
|
+
_MEMORY_SAVER = InMemorySaver()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@asynccontextmanager
|
|
35
|
+
async def open_checkpointer(
|
|
36
|
+
conninfo: str | None, *, setup: bool = False
|
|
37
|
+
) -> AsyncIterator[BaseCheckpointSaver]:
|
|
38
|
+
"""Yield the checkpointer for this environment, holding its resources for the ``with`` body.
|
|
39
|
+
|
|
40
|
+
``conninfo`` is the Postgres connection string (typically ``AGENT_SESSION_STORE_URI``). Blank
|
|
41
|
+
or ``None`` → an :class:`InMemorySaver` (no cross-process durability, fine for dev/tests). A
|
|
42
|
+
real connection string → an ``AsyncPostgresSaver`` bound to a live pool that is opened on enter
|
|
43
|
+
and closed on exit. ``setup=True`` runs the saver's table DDL first (gated migration; Postgres
|
|
44
|
+
only — the in-memory saver needs no schema).
|
|
45
|
+
"""
|
|
46
|
+
if not conninfo:
|
|
47
|
+
# Shared, not fresh — so an in-process run→resume sees the same checkpoints.
|
|
48
|
+
yield _MEMORY_SAVER
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
# Imported lazily: psycopg + langgraph-checkpoint-postgres are the optional [postgres] extra,
|
|
52
|
+
# so a bare install that never touches durability does not need them present.
|
|
53
|
+
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
|
54
|
+
|
|
55
|
+
async with AsyncPostgresSaver.from_conn_string(conninfo) as saver:
|
|
56
|
+
if setup:
|
|
57
|
+
await saver.setup()
|
|
58
|
+
yield saver
|