aigp-langchain 1.0.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.
@@ -0,0 +1,40 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .eggs/
9
+
10
+ # Node
11
+ node_modules/
12
+ dist/
13
+
14
+ # Go
15
+ bin/
16
+
17
+ # Rust
18
+ target/
19
+
20
+ # .NET
21
+ bin/
22
+ obj/
23
+
24
+ # Java
25
+ *.class
26
+ out/
27
+
28
+ # IDE
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+
37
+ # Secrets (never commit)
38
+ .env
39
+ *.pem
40
+ *.key
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: aigp-langchain
3
+ Version: 1.0.0
4
+ Summary: AIGP governance adapter for LangChain/LangGraph
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: aigp-agent-core>=1.0.0
@@ -0,0 +1,7 @@
1
+ """AIGP-LangChain — Governance adapter for LangChain/LangGraph."""
2
+
3
+ __version__ = "1.0.0"
4
+
5
+ from .handler import AigpCallbackHandler
6
+
7
+ __all__ = ["AigpCallbackHandler"]
@@ -0,0 +1,100 @@
1
+ """LangChain Callback Handler — maps LangChain lifecycle to AIGP governance."""
2
+
3
+ import asyncio
4
+ import logging
5
+ import time
6
+ from typing import Any
7
+ from uuid import UUID
8
+
9
+ from aigp_agent_core import AgentGovernance
10
+ from aigp_agent_core.governance import GovernanceBlockedError
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class AigpCallbackHandler:
16
+ """LangChain BaseCallbackHandler implementing AIGP governance.
17
+
18
+ Usage:
19
+ handler = AigpCallbackHandler(gov_url=..., app_id=..., hmac_secret=...)
20
+ agent = AgentExecutor(agent=..., tools=[...], callbacks=[handler])
21
+ """
22
+
23
+ def __init__(self, gov_url: str, app_id: str, hmac_secret: str, **kwargs):
24
+ self._gov = AgentGovernance(gov_url, app_id, hmac_secret, **kwargs)
25
+ self._root_run_id: UUID | None = None
26
+ self._tool_starts: dict[UUID, float] = {}
27
+
28
+ # --- Chain events ---
29
+
30
+ def on_chain_start(self, serialized: dict, inputs: dict, *, run_id: UUID, parent_run_id: UUID | None = None, **kwargs):
31
+ """Root chain start → AIGP CHECK."""
32
+ if parent_run_id is None:
33
+ self._root_run_id = run_id
34
+ agent_name = serialized.get("name", serialized.get("id", ["agent"])[-1])
35
+ model_id = kwargs.get("model_id", "unknown")
36
+ self._gov.pre_invoke(agent_name, model_id, kwargs.get("user_id", ""))
37
+
38
+ def on_chain_end(self, outputs: dict, *, run_id: UUID, parent_run_id: UUID | None = None, **kwargs):
39
+ """Root chain end → RECORD + TRACE + evidence."""
40
+ if parent_run_id is None and run_id == self._root_run_id:
41
+ self._run_async(self._gov.post_invoke())
42
+
43
+ def on_chain_error(self, error: BaseException, *, run_id: UUID, parent_run_id: UUID | None = None, **kwargs):
44
+ """Root chain error → RECORD with ERROR."""
45
+ if parent_run_id is None:
46
+ self._run_async(self._gov.on_error(error))
47
+
48
+ # --- LLM events ---
49
+
50
+ def on_llm_start(self, serialized: dict, prompts: list[str], *, run_id: UUID, **kwargs):
51
+ """LLM invocation starting."""
52
+ pass
53
+
54
+ def on_llm_end(self, response: Any, *, run_id: UUID, **kwargs):
55
+ """LLM invocation complete — accumulate tokens."""
56
+ usage = {}
57
+ if hasattr(response, "llm_output") and response.llm_output:
58
+ usage = response.llm_output.get("usage", response.llm_output.get("token_usage", {}))
59
+ elif hasattr(response, "usage_metadata") and response.usage_metadata:
60
+ usage = response.usage_metadata
61
+ self._gov.on_model_call(usage=usage)
62
+
63
+ def on_llm_error(self, error: BaseException, *, run_id: UUID, **kwargs):
64
+ pass
65
+
66
+ # --- Tool events ---
67
+
68
+ def on_tool_start(self, serialized: dict, input_str: str, *, run_id: UUID, **kwargs):
69
+ """Tool execution starting."""
70
+ self._tool_starts[run_id] = time.time()
71
+
72
+ def on_tool_end(self, output: str, *, run_id: UUID, **kwargs):
73
+ """Tool execution complete."""
74
+ start = self._tool_starts.pop(run_id, time.time())
75
+ duration_ms = int((time.time() - start) * 1000)
76
+ tool_name = kwargs.get("name", "unknown_tool")
77
+ self._run_async(self._gov.on_tool_call(tool_name, duration_ms=duration_ms))
78
+
79
+ def on_tool_error(self, error: BaseException, *, run_id: UUID, **kwargs):
80
+ self._tool_starts.pop(run_id, None)
81
+
82
+ # --- Retriever events ---
83
+
84
+ def on_retriever_start(self, serialized: dict, query: str, *, run_id: UUID, **kwargs):
85
+ pass
86
+
87
+ def on_retriever_end(self, documents: list, *, run_id: UUID, **kwargs):
88
+ pass
89
+
90
+ # --- Helpers ---
91
+
92
+ def _run_async(self, coro):
93
+ try:
94
+ loop = asyncio.get_event_loop()
95
+ if loop.is_running():
96
+ asyncio.ensure_future(coro)
97
+ else:
98
+ loop.run_until_complete(coro)
99
+ except RuntimeError:
100
+ asyncio.run(coro)
@@ -0,0 +1,10 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "aigp-langchain"
7
+ version = "1.0.0"
8
+ description = "AIGP governance adapter for LangChain/LangGraph"
9
+ requires-python = ">=3.11"
10
+ dependencies = ["aigp-agent-core>=1.0.0"]