agentproc 0.1.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,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentproc
3
+ Version: 0.1.0
4
+ Summary: AgentProc SDK — connect any Agent CLI to a messaging platform with a single function
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/jeffkit/agentproc
7
+ Project-URL: Documentation, https://agentproc.dev
8
+ Project-URL: Repository, https://github.com/jeffkit/agentproc
9
+ Project-URL: Issues, https://github.com/jeffkit/agentproc/issues
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agentproc"
7
+ version = "0.1.0"
8
+ description = "AgentProc SDK — connect any Agent CLI to a messaging platform with a single function"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ dependencies = []
13
+
14
+ [project.urls]
15
+ Homepage = "https://github.com/jeffkit/agentproc"
16
+ Documentation = "https://agentproc.dev"
17
+ Repository = "https://github.com/jeffkit/agentproc"
18
+ Issues = "https://github.com/jeffkit/agentproc/issues"
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,208 @@
1
+ """
2
+ agentproc — AgentProc Protocol SDK (Python)
3
+
4
+ Implements the AgentProc P0 protocol so you can write a single async handler
5
+ instead of manually reading env vars and formatting stdout.
6
+
7
+ Protocol contract:
8
+ Input — env vars: AGENT_MESSAGE, AGENT_SESSION_ID, AGENT_SESSION_NAME,
9
+ AGENT_FROM_USER, AGENT_STREAMING
10
+ Output — stdout:
11
+ optional first line "AGENT_SESSION:<uuid>"
12
+ optional partial lines "AGENT_PARTIAL:<json-string>"
13
+ remaining lines = final reply text
14
+ Exit — 0 = success, non-zero = error
15
+
16
+ Example::
17
+
18
+ from agentproc import create_profile
19
+
20
+ async def handler(ctx):
21
+ reply = await my_llm(ctx.message)
22
+ return reply
23
+
24
+ create_profile(handler)
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import asyncio
30
+ import json
31
+ import os
32
+ import sys
33
+ from dataclasses import dataclass, field
34
+ from datetime import datetime, timezone
35
+ from pathlib import Path
36
+ from typing import Awaitable, Callable, List, Optional, Union
37
+
38
+ __all__ = [
39
+ "AgentContext",
40
+ "AgentResult",
41
+ "create_profile",
42
+ "load_history",
43
+ "append_history",
44
+ "HistoryEntry",
45
+ ]
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Data classes
50
+ # ---------------------------------------------------------------------------
51
+
52
+ @dataclass
53
+ class AgentContext:
54
+ """Input context passed to the agent handler."""
55
+
56
+ message: str
57
+ """User message text (AGENT_MESSAGE)."""
58
+
59
+ session_id: str
60
+ """Session UUID from the previous turn (AGENT_SESSION_ID). Empty = new session."""
61
+
62
+ session_name: str
63
+ """Human-readable session name (AGENT_SESSION_NAME)."""
64
+
65
+ from_user: str
66
+ """Sender identifier (AGENT_FROM_USER)."""
67
+
68
+ streaming: bool
69
+ """Whether the bridge expects streaming output (AGENT_STREAMING == "1")."""
70
+
71
+ image_url: str
72
+ """Image attachment URL (AGENT_IMAGE_URL). Empty if no image."""
73
+
74
+ file_url: str
75
+ """File attachment URL (AGENT_FILE_URL). Empty if no file."""
76
+
77
+ async def send_partial(self, text: str) -> None:
78
+ """Send a streaming chunk to the user immediately.
79
+
80
+ Writes an ``AGENT_PARTIAL:<json>`` line to stdout and flushes.
81
+ The bridge forwards it to the user without waiting for the process to exit.
82
+
83
+ Has no effect when ``streaming`` is False (bridge will ignore the line).
84
+ """
85
+ if not text:
86
+ return
87
+ line = f"AGENT_PARTIAL:{json.dumps(text, ensure_ascii=False)}\n"
88
+ sys.stdout.write(line)
89
+ sys.stdout.flush()
90
+
91
+
92
+ @dataclass
93
+ class AgentResult:
94
+ """Return value from the agent handler."""
95
+
96
+ response: str = ""
97
+ """Final reply text. Can be empty if all content was sent via send_partial."""
98
+
99
+ session_id: str = ""
100
+ """CLI session UUID to persist. Bridge will pass it back next turn as AGENT_SESSION_ID."""
101
+
102
+
103
+ @dataclass
104
+ class HistoryEntry:
105
+ role: str
106
+ content: str
107
+ timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # History helpers (JSONL, stored per session)
112
+ # ---------------------------------------------------------------------------
113
+
114
+ def session_file_path(session_id: str, base_dir: Optional[str] = None) -> Path:
115
+ if not session_id:
116
+ raise ValueError("session_id must be non-empty")
117
+ root = Path(base_dir) if base_dir else Path.home() / ".agentproc" / "sessions"
118
+ root.mkdir(parents=True, exist_ok=True)
119
+ return root / f"{session_id}.jsonl"
120
+
121
+
122
+ def load_history(session_id: str, base_dir: Optional[str] = None) -> List[HistoryEntry]:
123
+ if not session_id:
124
+ return []
125
+ path = session_file_path(session_id, base_dir)
126
+ if not path.exists():
127
+ return []
128
+ entries: List[HistoryEntry] = []
129
+ for line in path.read_text(encoding="utf-8").splitlines():
130
+ line = line.strip()
131
+ if not line:
132
+ continue
133
+ try:
134
+ d = json.loads(line)
135
+ entries.append(HistoryEntry(
136
+ role=d.get("role", ""),
137
+ content=d.get("content", ""),
138
+ timestamp=d.get("timestamp", ""),
139
+ ))
140
+ except json.JSONDecodeError:
141
+ continue
142
+ return entries
143
+
144
+
145
+ def append_history(
146
+ session_id: str,
147
+ entries: List[HistoryEntry],
148
+ base_dir: Optional[str] = None,
149
+ ) -> None:
150
+ if not session_id or not entries:
151
+ return
152
+ path = session_file_path(session_id, base_dir)
153
+ with path.open("a", encoding="utf-8") as f:
154
+ for e in entries:
155
+ f.write(json.dumps({"role": e.role, "content": e.content, "timestamp": e.timestamp}, ensure_ascii=False))
156
+ f.write("\n")
157
+
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # Core entrypoint
161
+ # ---------------------------------------------------------------------------
162
+
163
+ Handler = Callable[[AgentContext], Awaitable[Union[str, AgentResult]]]
164
+
165
+
166
+ def create_profile(handler: Handler) -> None:
167
+ """Run the handler as an AgentProc-compliant process.
168
+
169
+ Reads AGENT_* env vars, calls the handler, and writes the P0 output to stdout.
170
+ Call this at the bottom of your script — it blocks until the handler completes
171
+ and then exits the process.
172
+
173
+ Args:
174
+ handler: An async function that takes an AgentContext and returns either
175
+ a plain string or an AgentResult.
176
+ """
177
+ ctx = AgentContext(
178
+ message=os.environ.get("AGENT_MESSAGE", ""),
179
+ session_id=os.environ.get("AGENT_SESSION_ID", ""),
180
+ session_name=os.environ.get("AGENT_SESSION_NAME", "default"),
181
+ from_user=os.environ.get("AGENT_FROM_USER", ""),
182
+ streaming=os.environ.get("AGENT_STREAMING", "1") != "0",
183
+ image_url=os.environ.get("AGENT_IMAGE_URL", ""),
184
+ file_url=os.environ.get("AGENT_FILE_URL", ""),
185
+ )
186
+
187
+ try:
188
+ result = asyncio.run(handler(ctx))
189
+ except Exception as e:
190
+ sys.stderr.write(f"[agentproc] handler error: {e}\n")
191
+ sys.exit(1)
192
+
193
+ if isinstance(result, str):
194
+ result = AgentResult(response=result)
195
+
196
+ # Emit session line first if we have one
197
+ if result.session_id:
198
+ sys.stdout.write(f"AGENT_SESSION:{result.session_id}\n")
199
+ sys.stdout.flush()
200
+
201
+ # Emit final reply body
202
+ if result.response:
203
+ sys.stdout.write(result.response)
204
+ if not result.response.endswith("\n"):
205
+ sys.stdout.write("\n")
206
+ sys.stdout.flush()
207
+
208
+ sys.exit(0)
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentproc
3
+ Version: 0.1.0
4
+ Summary: AgentProc SDK — connect any Agent CLI to a messaging platform with a single function
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/jeffkit/agentproc
7
+ Project-URL: Documentation, https://agentproc.dev
8
+ Project-URL: Repository, https://github.com/jeffkit/agentproc
9
+ Project-URL: Issues, https://github.com/jeffkit/agentproc/issues
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
@@ -0,0 +1,6 @@
1
+ pyproject.toml
2
+ src/agentproc/__init__.py
3
+ src/agentproc.egg-info/PKG-INFO
4
+ src/agentproc.egg-info/SOURCES.txt
5
+ src/agentproc.egg-info/dependency_links.txt
6
+ src/agentproc.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ agentproc