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.
- agentproc-0.1.0/PKG-INFO +11 -0
- agentproc-0.1.0/pyproject.toml +21 -0
- agentproc-0.1.0/setup.cfg +4 -0
- agentproc-0.1.0/src/agentproc/__init__.py +208 -0
- agentproc-0.1.0/src/agentproc.egg-info/PKG-INFO +11 -0
- agentproc-0.1.0/src/agentproc.egg-info/SOURCES.txt +6 -0
- agentproc-0.1.0/src/agentproc.egg-info/dependency_links.txt +1 -0
- agentproc-0.1.0/src/agentproc.egg-info/top_level.txt +1 -0
agentproc-0.1.0/PKG-INFO
ADDED
|
@@ -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,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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agentproc
|