agent-runtime-core 0.1.0__py3-none-any.whl

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,158 @@
1
+ """
2
+ SQLite state store implementation.
3
+
4
+ Good for:
5
+ - Local development with persistence
6
+ - Single-process deployments
7
+ - Testing with real database
8
+ """
9
+
10
+ import json
11
+ import sqlite3
12
+ from contextlib import contextmanager
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+ from typing import Optional
16
+ from uuid import UUID
17
+
18
+ from agent_runtime.state.base import StateStore, Checkpoint
19
+
20
+
21
+ class SQLiteStateStore(StateStore):
22
+ """
23
+ SQLite-backed state store.
24
+
25
+ Stores checkpoints in a local SQLite database.
26
+ Supports both file-based and in-memory databases.
27
+ """
28
+
29
+ def __init__(self, path: str = "agent_runtime.db"):
30
+ """
31
+ Initialize SQLite state store.
32
+
33
+ Args:
34
+ path: Path to SQLite database file, or ":memory:" for in-memory
35
+ """
36
+ self.path = path
37
+ self._initialized = False
38
+
39
+ @contextmanager
40
+ def _get_connection(self):
41
+ """Get a database connection."""
42
+ conn = sqlite3.connect(self.path)
43
+ conn.row_factory = sqlite3.Row
44
+ try:
45
+ yield conn
46
+ finally:
47
+ conn.close()
48
+
49
+ def _ensure_initialized(self):
50
+ """Ensure the database schema exists."""
51
+ if self._initialized:
52
+ return
53
+
54
+ with self._get_connection() as conn:
55
+ conn.execute("""
56
+ CREATE TABLE IF NOT EXISTS checkpoints (
57
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
58
+ run_id TEXT NOT NULL,
59
+ seq INTEGER NOT NULL,
60
+ state TEXT NOT NULL,
61
+ created_at TEXT NOT NULL,
62
+ UNIQUE(run_id, seq)
63
+ )
64
+ """)
65
+ conn.execute("""
66
+ CREATE INDEX IF NOT EXISTS idx_checkpoints_run_id
67
+ ON checkpoints(run_id)
68
+ """)
69
+ conn.commit()
70
+
71
+ self._initialized = True
72
+
73
+ async def save_checkpoint(self, checkpoint: Checkpoint) -> None:
74
+ """Save a checkpoint."""
75
+ self._ensure_initialized()
76
+
77
+ with self._get_connection() as conn:
78
+ conn.execute(
79
+ """
80
+ INSERT OR REPLACE INTO checkpoints (run_id, seq, state, created_at)
81
+ VALUES (?, ?, ?, ?)
82
+ """,
83
+ (
84
+ str(checkpoint.run_id),
85
+ checkpoint.seq,
86
+ json.dumps(checkpoint.state),
87
+ checkpoint.created_at.isoformat(),
88
+ ),
89
+ )
90
+ conn.commit()
91
+
92
+ async def get_latest_checkpoint(self, run_id: UUID) -> Optional[Checkpoint]:
93
+ """Get the latest checkpoint for a run."""
94
+ self._ensure_initialized()
95
+
96
+ with self._get_connection() as conn:
97
+ row = conn.execute(
98
+ """
99
+ SELECT run_id, seq, state, created_at
100
+ FROM checkpoints
101
+ WHERE run_id = ?
102
+ ORDER BY seq DESC
103
+ LIMIT 1
104
+ """,
105
+ (str(run_id),),
106
+ ).fetchone()
107
+
108
+ if not row:
109
+ return None
110
+
111
+ return Checkpoint(
112
+ run_id=UUID(row["run_id"]),
113
+ seq=row["seq"],
114
+ state=json.loads(row["state"]),
115
+ created_at=datetime.fromisoformat(row["created_at"]),
116
+ )
117
+
118
+ async def get_checkpoints(self, run_id: UUID) -> list[Checkpoint]:
119
+ """Get all checkpoints for a run."""
120
+ self._ensure_initialized()
121
+
122
+ with self._get_connection() as conn:
123
+ rows = conn.execute(
124
+ """
125
+ SELECT run_id, seq, state, created_at
126
+ FROM checkpoints
127
+ WHERE run_id = ?
128
+ ORDER BY seq ASC
129
+ """,
130
+ (str(run_id),),
131
+ ).fetchall()
132
+
133
+ return [
134
+ Checkpoint(
135
+ run_id=UUID(row["run_id"]),
136
+ seq=row["seq"],
137
+ state=json.loads(row["state"]),
138
+ created_at=datetime.fromisoformat(row["created_at"]),
139
+ )
140
+ for row in rows
141
+ ]
142
+
143
+ async def get_next_seq(self, run_id: UUID) -> int:
144
+ """Get the next sequence number for a run."""
145
+ latest = await self.get_latest_checkpoint(run_id)
146
+ return (latest.seq + 1) if latest else 0
147
+
148
+ async def delete_checkpoints(self, run_id: UUID) -> int:
149
+ """Delete all checkpoints for a run."""
150
+ self._ensure_initialized()
151
+
152
+ with self._get_connection() as conn:
153
+ cursor = conn.execute(
154
+ "DELETE FROM checkpoints WHERE run_id = ?",
155
+ (str(run_id),),
156
+ )
157
+ conn.commit()
158
+ return cursor.rowcount
@@ -0,0 +1,47 @@
1
+ """
2
+ Tracing implementations for observability.
3
+
4
+ Provides:
5
+ - TraceSink: Abstract interface
6
+ - NoopTraceSink: No-op implementation
7
+ - LangfuseTraceSink: Langfuse integration
8
+ """
9
+
10
+ from agent_runtime.tracing.noop import NoopTraceSink
11
+
12
+ __all__ = [
13
+ "NoopTraceSink",
14
+ "get_trace_sink",
15
+ ]
16
+
17
+
18
+ def get_trace_sink(backend: str = None, **kwargs):
19
+ """
20
+ Factory function to get a trace sink.
21
+
22
+ Args:
23
+ backend: "noop" or "langfuse"
24
+ **kwargs: Backend-specific configuration
25
+
26
+ Returns:
27
+ TraceSink instance
28
+ """
29
+ from agent_runtime.config import get_config
30
+ from agent_runtime.interfaces import TraceSink
31
+
32
+ config = get_config()
33
+ backend = backend or config.tracing_backend or "noop"
34
+
35
+ if backend == "noop":
36
+ return NoopTraceSink()
37
+
38
+ elif backend == "langfuse":
39
+ from agent_runtime.tracing.langfuse import LangfuseTraceSink
40
+ return LangfuseTraceSink(
41
+ public_key=kwargs.get("public_key") or config.langfuse_public_key,
42
+ secret_key=kwargs.get("secret_key") or config.langfuse_secret_key,
43
+ host=kwargs.get("host") or config.langfuse_host,
44
+ )
45
+
46
+ else:
47
+ raise ValueError(f"Unknown tracing backend: {backend}")
@@ -0,0 +1,119 @@
1
+ """
2
+ Langfuse trace sink implementation.
3
+
4
+ Langfuse is an open-source LLM observability platform.
5
+ This is an OPTIONAL integration - the core runtime doesn't depend on it.
6
+
7
+ See: https://langfuse.com/
8
+ """
9
+
10
+ import logging
11
+ from typing import Optional
12
+ from uuid import UUID
13
+
14
+ from agent_runtime.interfaces import TraceSink
15
+
16
+ try:
17
+ from langfuse import Langfuse
18
+ except ImportError:
19
+ Langfuse = None
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class LangfuseTraceSink(TraceSink):
25
+ """
26
+ Langfuse trace sink for LLM observability.
27
+
28
+ Sends traces to Langfuse for monitoring, debugging, and analytics.
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ public_key: Optional[str] = None,
34
+ secret_key: Optional[str] = None,
35
+ host: Optional[str] = None,
36
+ ):
37
+ if Langfuse is None:
38
+ raise ImportError(
39
+ "langfuse package is required for LangfuseTraceSink. "
40
+ "Install with: pip install agent_runtime[langfuse]"
41
+ )
42
+
43
+ self._client = Langfuse(
44
+ public_key=public_key,
45
+ secret_key=secret_key,
46
+ host=host,
47
+ )
48
+ self._traces: dict[UUID, any] = {}
49
+
50
+ def start_run(self, run_id: UUID, metadata: dict) -> None:
51
+ """Start a new trace in Langfuse."""
52
+ try:
53
+ trace = self._client.trace(
54
+ id=str(run_id),
55
+ name=metadata.get("agent_key", "agent_run"),
56
+ metadata=metadata,
57
+ )
58
+ self._traces[run_id] = trace
59
+ except Exception as e:
60
+ logger.warning(f"Failed to start Langfuse trace: {e}")
61
+
62
+ def log_event(self, run_id: UUID, event_type: str, payload: dict) -> None:
63
+ """Log an event to the trace."""
64
+ trace = self._traces.get(run_id)
65
+ if not trace:
66
+ return
67
+
68
+ try:
69
+ # Map event types to Langfuse concepts
70
+ if event_type == "assistant.message":
71
+ trace.generation(
72
+ name="assistant_message",
73
+ output=payload.get("content", ""),
74
+ metadata=payload,
75
+ )
76
+ elif event_type == "tool.call":
77
+ trace.span(
78
+ name=f"tool:{payload.get('name', 'unknown')}",
79
+ input=payload.get("arguments", {}),
80
+ )
81
+ elif event_type == "tool.result":
82
+ # Tool results are logged as part of the span
83
+ pass
84
+ else:
85
+ # Generic event
86
+ trace.event(
87
+ name=event_type,
88
+ metadata=payload,
89
+ )
90
+ except Exception as e:
91
+ logger.warning(f"Failed to log Langfuse event: {e}")
92
+
93
+ def end_run(self, run_id: UUID, outcome: str, metadata: Optional[dict] = None) -> None:
94
+ """End the trace."""
95
+ trace = self._traces.pop(run_id, None)
96
+ if not trace:
97
+ return
98
+
99
+ try:
100
+ # Update trace with final status
101
+ status_map = {
102
+ "succeeded": "SUCCESS",
103
+ "failed": "ERROR",
104
+ "cancelled": "CANCELLED",
105
+ "timed_out": "ERROR",
106
+ }
107
+ trace.update(
108
+ status=status_map.get(outcome, "UNKNOWN"),
109
+ metadata=metadata or {},
110
+ )
111
+ except Exception as e:
112
+ logger.warning(f"Failed to end Langfuse trace: {e}")
113
+
114
+ def flush(self) -> None:
115
+ """Flush any buffered traces to Langfuse."""
116
+ try:
117
+ self._client.flush()
118
+ except Exception as e:
119
+ logger.warning(f"Failed to flush Langfuse traces: {e}")
@@ -0,0 +1,34 @@
1
+ """
2
+ No-op trace sink implementation.
3
+
4
+ Used when tracing is disabled or not configured.
5
+ """
6
+
7
+ from typing import Optional
8
+ from uuid import UUID
9
+
10
+ from agent_runtime.interfaces import TraceSink
11
+
12
+
13
+ class NoopTraceSink(TraceSink):
14
+ """
15
+ No-op trace sink that discards all traces.
16
+
17
+ Used when tracing is disabled.
18
+ """
19
+
20
+ def start_run(self, run_id: UUID, metadata: dict) -> None:
21
+ """No-op."""
22
+ pass
23
+
24
+ def log_event(self, run_id: UUID, event_type: str, payload: dict) -> None:
25
+ """No-op."""
26
+ pass
27
+
28
+ def end_run(self, run_id: UUID, outcome: str, metadata: Optional[dict] = None) -> None:
29
+ """No-op."""
30
+ pass
31
+
32
+ def flush(self) -> None:
33
+ """No-op."""
34
+ pass
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-runtime-core
3
+ Version: 0.1.0
4
+ Summary: Framework-agnostic Python library for executing AI agents with consistent patterns
5
+ Project-URL: Homepage, https://github.com/colstrom/agent_runtime
6
+ Project-URL: Repository, https://github.com/colstrom/agent_runtime
7
+ Author: Chris Olstrom
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents,ai,async,llm,runtime
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.11
20
+ Provides-Extra: all
21
+ Requires-Dist: anthropic>=0.18.0; extra == 'all'
22
+ Requires-Dist: langfuse>=2.0.0; extra == 'all'
23
+ Requires-Dist: litellm>=1.0.0; extra == 'all'
24
+ Requires-Dist: openai>=1.0.0; extra == 'all'
25
+ Requires-Dist: redis>=5.0.0; extra == 'all'
26
+ Provides-Extra: anthropic
27
+ Requires-Dist: anthropic>=0.18.0; extra == 'anthropic'
28
+ Provides-Extra: dev
29
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
34
+ Provides-Extra: langfuse
35
+ Requires-Dist: langfuse>=2.0.0; extra == 'langfuse'
36
+ Provides-Extra: litellm
37
+ Requires-Dist: litellm>=1.0.0; extra == 'litellm'
38
+ Provides-Extra: openai
39
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
40
+ Provides-Extra: redis
41
+ Requires-Dist: redis>=5.0.0; extra == 'redis'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # agent_runtime
45
+
46
+ Framework-agnostic agent runtime library for Python.
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install agent_runtime
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```python
57
+ from agent_runtime import configure, get_llm_client
58
+
59
+ # Configure
60
+ configure(
61
+ model_provider="openai",
62
+ openai_api_key="sk-..."
63
+ )
64
+
65
+ # Get LLM client
66
+ llm = get_llm_client()
67
+ ```
68
+
69
+ ## Features
70
+
71
+ - Pluggable backends (memory, redis, sqlite)
72
+ - LLM client abstractions (OpenAI, Anthropic, LiteLLM)
73
+ - Event streaming
74
+ - State management
75
+ - Queue-based execution
@@ -0,0 +1,31 @@
1
+ agent_runtime/__init__.py,sha256=wuWpDXsLUB57Psgd97MrjkRoINvO6NuxUwZQZUON3aU,2098
2
+ agent_runtime/config.py,sha256=ZRjpILjsjeh_kl7873DtV2g_zaTrfdkb3NgdQ6ndb5Y,4981
3
+ agent_runtime/interfaces.py,sha256=AGDY0w6muQnNiice9O3ogb8fRdgMRW6wqXpxcuyn0N0,10103
4
+ agent_runtime/registry.py,sha256=sa0speDFxFCZlXoCge8cPNqWYUeWHyazs6tBer5Jg1w,1471
5
+ agent_runtime/runner.py,sha256=Sb2FfSJvATaL7ideQZy2JhVZp0sSYGVIov93E-gxODU,12741
6
+ agent_runtime/events/__init__.py,sha256=JNH-D40O6yz2evIf1_r2o3w7OQjLt4Yebn-sBNLzzh8,1550
7
+ agent_runtime/events/base.py,sha256=NfHYyoczxr40Er5emROi_aY_07m5hDrKsn31pdWY2DY,1950
8
+ agent_runtime/events/memory.py,sha256=7qseR6RtdaP833FxEHwyPw5TC7l4brJHr8uEx0mLc1Y,2486
9
+ agent_runtime/events/redis.py,sha256=Z6WEvp_6jcIPi4ZgkGk5J61qxgGqllwk7jqJM4jcTXk,5869
10
+ agent_runtime/events/sqlite.py,sha256=EiX1BMOqeS7BelmD8A6cvhz3fE4w7vJ2Wg4pFu1V2u0,5092
11
+ agent_runtime/llm/__init__.py,sha256=JEk1Q2H6U9_Uid48YVm1wYR1W7po0vtjfjf2TTmQe_A,2431
12
+ agent_runtime/llm/anthropic.py,sha256=R5KIcHAvHwoibkI0bXxg0RXlwd1qRGcdlGEEEoApVCM,7491
13
+ agent_runtime/llm/litellm_client.py,sha256=M_-dAJVMRfWeNJlB0HXpFykbiIDRGwq1uMnamO3XUr8,5178
14
+ agent_runtime/llm/openai.py,sha256=eL6_lhsUvwlE24TfDnzmlF285M0tExjb00rJV43oz28,6837
15
+ agent_runtime/queue/__init__.py,sha256=78k29iEl8brp71LrOnmTHhQzPMHkzGre-Xqdl1NlNr0,1502
16
+ agent_runtime/queue/base.py,sha256=QW1eWbwBX_tmVD8yJobFJtlxLd_RtUWHTuXGessuxy8,3959
17
+ agent_runtime/queue/memory.py,sha256=n7kiE0Fw_BFUdzMyoO1QPO0ATzz8zBYaMQex7GdceZw,5411
18
+ agent_runtime/queue/redis.py,sha256=buTCgqNz77q7ae_eNIRRUEyhx_jCgJ0Q3Bqe1Hz5G-s,15617
19
+ agent_runtime/queue/sqlite.py,sha256=DyV2h3C5WuI9d_FjOmleeYvL7BbttMHN1E9XcK-X15w,14333
20
+ agent_runtime/state/__init__.py,sha256=W9juyZD8N0zj5ERkWroW7O0SLWnoYFcLjGuR4tfFKs4,1539
21
+ agent_runtime/state/base.py,sha256=NqE3B0ySa-U2jkelgmkBbkmkaIQxfu4pDryoxkZTMrc,1593
22
+ agent_runtime/state/memory.py,sha256=xOnlqM3ArXDKAdPx3PxdS9IGgJDSM-EKp_S1Hsit180,1561
23
+ agent_runtime/state/redis.py,sha256=-lPi_2xKm7Bc4DVMJfSEAF7wJHctLV3ZMM9AYBeQKZU,3425
24
+ agent_runtime/state/sqlite.py,sha256=NwuiTBXELb2tyOoH91MZqRJaCk9h8PskyY2VUc5EMr0,4868
25
+ agent_runtime/tracing/__init__.py,sha256=m4WzfgJpnV5XCCoMpBYZdJU_JTkAdhEhl7M7tpf62RY,1246
26
+ agent_runtime/tracing/langfuse.py,sha256=uThF0P6f1VJ1l1b7UuiFQ-oHZ-tCa9MbbHvTqkSuQ2A,3650
27
+ agent_runtime/tracing/noop.py,sha256=MOm5eTrnf3d4WhiWrwVU5Kd3GmJ1903V0U7U3Qwho7U,746
28
+ agent_runtime_core-0.1.0.dist-info/METADATA,sha256=js52zDqOlTnalHxspGoPZ31vcOHZAMINC8ln5dLC72Y,2274
29
+ agent_runtime_core-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
30
+ agent_runtime_core-0.1.0.dist-info/licenses/LICENSE,sha256=PcOO8aiOZ4H2MWYeKIis3o6xTCT1hNkDyCxHZhh1NeM,1070
31
+ agent_runtime_core-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chris Olstrom
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.