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.
Files changed (65) hide show
  1. agentship_langgraph-0.0.1/.gitignore +27 -0
  2. agentship_langgraph-0.0.1/PKG-INFO +24 -0
  3. agentship_langgraph-0.0.1/README.md +3 -0
  4. agentship_langgraph-0.0.1/pyproject.toml +42 -0
  5. agentship_langgraph-0.0.1/src/agentship_langgraph/__init__.py +17 -0
  6. agentship_langgraph-0.0.1/src/agentship_langgraph/agent.py +77 -0
  7. agentship_langgraph-0.0.1/src/agentship_langgraph/durability.py +58 -0
  8. agentship_langgraph-0.0.1/src/agentship_langgraph/engine.py +725 -0
  9. agentship_langgraph-0.0.1/src/agentship_langgraph/mcp.py +159 -0
  10. agentship_langgraph-0.0.1/src/agentship_langgraph/mcp_auth.py +93 -0
  11. agentship_langgraph-0.0.1/src/agentship_langgraph/models.py +201 -0
  12. agentship_langgraph-0.0.1/src/agentship_langgraph/py.typed +0 -0
  13. agentship_langgraph-0.0.1/src/agentship_langgraph/studio.py +33 -0
  14. agentship_langgraph-0.0.1/src/agentship_langgraph/templates/__init__.py +68 -0
  15. agentship_langgraph-0.0.1/src/agentship_langgraph/templates/autonomous_tpl.py +86 -0
  16. agentship_langgraph-0.0.1/src/agentship_langgraph/templates/graph.py +100 -0
  17. agentship_langgraph-0.0.1/src/agentship_langgraph/templates/graph_config.py +76 -0
  18. agentship_langgraph-0.0.1/src/agentship_langgraph/templates/graph_supervisor.py +377 -0
  19. agentship_langgraph-0.0.1/src/agentship_langgraph/templates/single.py +40 -0
  20. agentship_langgraph-0.0.1/src/agentship_langgraph/testing.py +130 -0
  21. agentship_langgraph-0.0.1/src/agentship_langgraph/tools.py +185 -0
  22. agentship_langgraph-0.0.1/src/agentship_langgraph/tracing.py +414 -0
  23. agentship_langgraph-0.0.1/tests/cassettes/test_assistant_example/test_assistant_example_runs_live_via_cassette.yaml +137 -0
  24. agentship_langgraph-0.0.1/tests/cassettes/test_live_model/test_real_gpt_4o_mini_returns_a_non_empty_answer.yaml +137 -0
  25. agentship_langgraph-0.0.1/tests/cassettes/test_providers/openai.yaml +120 -0
  26. agentship_langgraph-0.0.1/tests/cassettes/test_streaming_live/test_real_gpt_4o_mini_streams_multiple_token_chunks.yaml +176 -0
  27. agentship_langgraph-0.0.1/tests/fixtures/mcp_echo_server.py +22 -0
  28. agentship_langgraph-0.0.1/tests/providers.py +54 -0
  29. agentship_langgraph-0.0.1/tests/test_assistant_example.py +52 -0
  30. agentship_langgraph-0.0.1/tests/test_authoring_examples.py +84 -0
  31. agentship_langgraph-0.0.1/tests/test_autonomous_template.py +100 -0
  32. agentship_langgraph-0.0.1/tests/test_custom_build_graph.py +119 -0
  33. agentship_langgraph-0.0.1/tests/test_declarative_supervisor.py +61 -0
  34. agentship_langgraph-0.0.1/tests/test_durability.py +57 -0
  35. agentship_langgraph-0.0.1/tests/test_graph_config.py +81 -0
  36. agentship_langgraph-0.0.1/tests/test_graph_supervisor.py +177 -0
  37. agentship_langgraph-0.0.1/tests/test_graph_template.py +79 -0
  38. agentship_langgraph-0.0.1/tests/test_langgraph_durable.py +153 -0
  39. agentship_langgraph-0.0.1/tests/test_langgraph_engine.py +284 -0
  40. agentship_langgraph-0.0.1/tests/test_langgraph_hitl.py +115 -0
  41. agentship_langgraph-0.0.1/tests/test_langgraph_registration.py +25 -0
  42. agentship_langgraph-0.0.1/tests/test_live_model.py +59 -0
  43. agentship_langgraph-0.0.1/tests/test_mcp.py +107 -0
  44. agentship_langgraph-0.0.1/tests/test_mcp_auth.py +53 -0
  45. agentship_langgraph-0.0.1/tests/test_mcp_discovery_timeout.py +73 -0
  46. agentship_langgraph-0.0.1/tests/test_mcp_tool_errors.py +110 -0
  47. agentship_langgraph-0.0.1/tests/test_mixed_engine_dispatch.py +52 -0
  48. agentship_langgraph-0.0.1/tests/test_models.py +75 -0
  49. agentship_langgraph-0.0.1/tests/test_otlp_wire_export.py +246 -0
  50. agentship_langgraph-0.0.1/tests/test_provider_examples.py +124 -0
  51. agentship_langgraph-0.0.1/tests/test_providers.py +102 -0
  52. agentship_langgraph-0.0.1/tests/test_resume_gate.py +48 -0
  53. agentship_langgraph-0.0.1/tests/test_router_purity.py +97 -0
  54. agentship_langgraph-0.0.1/tests/test_session_memory.py +75 -0
  55. agentship_langgraph-0.0.1/tests/test_single_template.py +149 -0
  56. agentship_langgraph-0.0.1/tests/test_stream_tool_events.py +75 -0
  57. agentship_langgraph-0.0.1/tests/test_streaming_live.py +87 -0
  58. agentship_langgraph-0.0.1/tests/test_streaming_sends_tokens_as_they_arrive.py +127 -0
  59. agentship_langgraph-0.0.1/tests/test_subagent_trace_shape.py +129 -0
  60. agentship_langgraph-0.0.1/tests/test_supervisor_stream_content.py +61 -0
  61. agentship_langgraph-0.0.1/tests/test_tool_execution.py +302 -0
  62. agentship_langgraph-0.0.1/tests/test_tracing_callback.py +147 -0
  63. agentship_langgraph-0.0.1/tests/test_tracing_callback_parity.py +137 -0
  64. agentship_langgraph-0.0.1/tests/test_tracing_engine_integration.py +174 -0
  65. 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,3 @@
1
+ # agentship-langgraph
2
+
3
+ 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