gateforge-sdk 0.2.3__tar.gz → 0.2.5__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.
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/PKG-INFO +2 -2
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/__init__.py +18 -1
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/context.py +29 -9
- gateforge_sdk-0.2.5/gateforge/session_manager.py +344 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/pyproject.toml +2 -2
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/tests/test_phase1.py +57 -31
- gateforge_sdk-0.2.5/tests/test_phase2.py +528 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/.env.example +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/.gitignore +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/.pypirc.example +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/INSTALL.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/LICENSE +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/MANIFEST.in +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/PUBLISHING.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/PUBLISH_NOW.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/README.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/ab/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/ab/engine.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/client.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/config.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/features/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/guardrails/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/guardrails/engine.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/metrics.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/options.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/pii.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/pricing.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/prompt.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/providers/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/providers/anthropic.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/providers/gemini.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/providers/openai.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/response.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/tracing.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/wrappers/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/wrappers/anthropic.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/wrappers/gemini.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/gateforge/wrappers/openai.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/tests/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/tests/test_metrics.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/tests/test_pii.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.5}/tests/test_providers.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gateforge-sdk
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary: Privacy-first LLMOps SDK — Auto-init, tool/agent decorators, session
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: Privacy-first LLMOps SDK — Auto-init, tool/agent decorators, session management, nested conversation support
|
|
5
5
|
Project-URL: Homepage, https://gateforge.dev
|
|
6
6
|
Project-URL: Documentation, https://gateforge.dev/docs
|
|
7
7
|
Project-URL: Repository, https://github.com/gateforge/gateforge-sdk
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.5"
|
|
4
4
|
|
|
5
5
|
# Auto-install spaCy model if missing (required for PII detection)
|
|
6
6
|
def _ensure_spacy_model():
|
|
@@ -26,6 +26,15 @@ from gateforge.response import GatforgeResponse
|
|
|
26
26
|
from gateforge.tracing import configure_otel
|
|
27
27
|
from gateforge.ab.engine import VariantAssignment
|
|
28
28
|
from gateforge.guardrails.engine import GuardrailViolation, GuardrailBlocked
|
|
29
|
+
from gateforge.session_manager import (
|
|
30
|
+
SessionManager,
|
|
31
|
+
SessionState,
|
|
32
|
+
get_session_manager,
|
|
33
|
+
create_session,
|
|
34
|
+
get_session,
|
|
35
|
+
get_current_conversation_id,
|
|
36
|
+
get_current_trace_info,
|
|
37
|
+
)
|
|
29
38
|
|
|
30
39
|
_client: GatforgeClient | None = None
|
|
31
40
|
|
|
@@ -426,6 +435,14 @@ __all__ = [
|
|
|
426
435
|
"agent",
|
|
427
436
|
"record_tool_call",
|
|
428
437
|
"record_tool_result",
|
|
438
|
+
# Session management (Phase 2)
|
|
439
|
+
"SessionManager",
|
|
440
|
+
"SessionState",
|
|
441
|
+
"get_session_manager",
|
|
442
|
+
"create_session",
|
|
443
|
+
"get_session",
|
|
444
|
+
"get_current_conversation_id",
|
|
445
|
+
"get_current_trace_info",
|
|
429
446
|
# OTel configuration
|
|
430
447
|
"configure_otel",
|
|
431
448
|
# Standalone PII helpers
|
|
@@ -183,6 +183,9 @@ def agent(
|
|
|
183
183
|
"""
|
|
184
184
|
Decorator that creates a trace context for an agent function.
|
|
185
185
|
|
|
186
|
+
If there's an active trace (e.g., from gateforge.session()), it uses that
|
|
187
|
+
conversation_id for proper nesting. Otherwise creates a new trace.
|
|
188
|
+
|
|
186
189
|
All LLM calls and tool calls inside are automatically traced.
|
|
187
190
|
|
|
188
191
|
Usage::
|
|
@@ -190,14 +193,27 @@ def agent(
|
|
|
190
193
|
def my_agent(message):
|
|
191
194
|
response = client.chat.completions.create(...)
|
|
192
195
|
return response
|
|
196
|
+
|
|
197
|
+
# Or inside a session - shares the session's conversation_id:
|
|
198
|
+
with gateforge.session() as sess:
|
|
199
|
+
my_agent("hello") # Uses sess.conversation_id
|
|
193
200
|
"""
|
|
194
201
|
def decorator(fn: Callable) -> Callable:
|
|
195
202
|
@functools.wraps(fn)
|
|
196
203
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
197
204
|
from gateforge.tracing import emit_trace_event
|
|
198
205
|
|
|
199
|
-
#
|
|
200
|
-
|
|
206
|
+
# Check if there's an active trace (e.g., from session())
|
|
207
|
+
active_trace = get_active_trace()
|
|
208
|
+
|
|
209
|
+
if active_trace:
|
|
210
|
+
# Use existing conversation_id for nesting
|
|
211
|
+
cid = active_trace.conversation_id
|
|
212
|
+
is_nested = True
|
|
213
|
+
else:
|
|
214
|
+
# Create new conversation_id
|
|
215
|
+
cid = conversation_id or f"{fn.__name__}-{int(time.time())}"
|
|
216
|
+
is_nested = False
|
|
201
217
|
|
|
202
218
|
# Emit agent start event
|
|
203
219
|
emit_trace_event(
|
|
@@ -206,12 +222,15 @@ def agent(
|
|
|
206
222
|
conversation_id=cid,
|
|
207
223
|
event_type="agent_start",
|
|
208
224
|
step=0,
|
|
209
|
-
metadata={"user_id": user_id, "args": _safe_str(args)[:200], "agent_name": name or fn.__name__},
|
|
225
|
+
metadata={"user_id": user_id, "args": _safe_str(args)[:200], "agent_name": name or fn.__name__, "nested": is_nested},
|
|
210
226
|
)
|
|
211
227
|
|
|
212
|
-
# Enter trace context
|
|
213
|
-
|
|
214
|
-
|
|
228
|
+
# Enter trace context (only if not already in one)
|
|
229
|
+
if not is_nested:
|
|
230
|
+
ctx = trace(conversation_id=cid)
|
|
231
|
+
ctx.__enter__()
|
|
232
|
+
else:
|
|
233
|
+
ctx = None # Already in a trace context
|
|
215
234
|
|
|
216
235
|
try:
|
|
217
236
|
result = fn(*args, **kwargs)
|
|
@@ -222,7 +241,7 @@ def agent(
|
|
|
222
241
|
base_url=_get_base_url(),
|
|
223
242
|
conversation_id=cid,
|
|
224
243
|
event_type="agent_end",
|
|
225
|
-
step=ctx.ctx.step,
|
|
244
|
+
step=active_trace.step if active_trace else ctx.ctx.step,
|
|
226
245
|
metadata={"success": True},
|
|
227
246
|
)
|
|
228
247
|
|
|
@@ -234,12 +253,13 @@ def agent(
|
|
|
234
253
|
base_url=_get_base_url(),
|
|
235
254
|
conversation_id=cid,
|
|
236
255
|
event_type="agent_error",
|
|
237
|
-
step=ctx.ctx.step,
|
|
256
|
+
step=active_trace.step if active_trace else ctx.ctx.step,
|
|
238
257
|
metadata={"error": str(e)[:500]},
|
|
239
258
|
)
|
|
240
259
|
raise
|
|
241
260
|
finally:
|
|
242
|
-
ctx
|
|
261
|
+
if ctx:
|
|
262
|
+
ctx.__exit__(None, None, None)
|
|
243
263
|
|
|
244
264
|
return wrapper
|
|
245
265
|
return decorator
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Phase 2: Session Management & Enhanced Trace Context
|
|
3
|
+
|
|
4
|
+
Provides:
|
|
5
|
+
- SessionManager for persistent session state
|
|
6
|
+
- Enhanced metadata support (user_id, tags, custom fields)
|
|
7
|
+
- Session persistence (save/load from dict)
|
|
8
|
+
- Conversation helpers
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import uuid
|
|
14
|
+
import time
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from typing import Optional, Dict, Any, List
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class SessionState:
|
|
22
|
+
"""
|
|
23
|
+
Represents the state of a conversation session.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
conversation_id: Unique identifier for the conversation
|
|
27
|
+
user_id: Optional user identifier
|
|
28
|
+
created_at: Unix timestamp when session was created
|
|
29
|
+
updated_at: Unix timestamp of last activity
|
|
30
|
+
message_count: Number of messages/turns in this conversation
|
|
31
|
+
tags: Optional tags for categorization
|
|
32
|
+
metadata: Custom metadata dictionary
|
|
33
|
+
"""
|
|
34
|
+
conversation_id: str
|
|
35
|
+
user_id: Optional[str] = None
|
|
36
|
+
created_at: float = field(default_factory=time.time)
|
|
37
|
+
updated_at: float = field(default_factory=time.time)
|
|
38
|
+
message_count: int = 0
|
|
39
|
+
tags: List[str] = field(default_factory=list)
|
|
40
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
41
|
+
|
|
42
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
43
|
+
"""Serialize session state to dictionary."""
|
|
44
|
+
return {
|
|
45
|
+
"conversation_id": self.conversation_id,
|
|
46
|
+
"user_id": self.user_id,
|
|
47
|
+
"created_at": self.created_at,
|
|
48
|
+
"updated_at": self.updated_at,
|
|
49
|
+
"message_count": self.message_count,
|
|
50
|
+
"tags": self.tags,
|
|
51
|
+
"metadata": self.metadata,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_dict(cls, data: Dict[str, Any]) -> SessionState:
|
|
56
|
+
"""Deserialize session state from dictionary."""
|
|
57
|
+
return cls(
|
|
58
|
+
conversation_id=data["conversation_id"],
|
|
59
|
+
user_id=data.get("user_id"),
|
|
60
|
+
created_at=data.get("created_at", time.time()),
|
|
61
|
+
updated_at=data.get("updated_at", time.time()),
|
|
62
|
+
message_count=data.get("message_count", 0),
|
|
63
|
+
tags=data.get("tags", []),
|
|
64
|
+
metadata=data.get("metadata", {}),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def touch(self) -> None:
|
|
68
|
+
"""Update the updated_at timestamp and increment message count."""
|
|
69
|
+
self.updated_at = time.time()
|
|
70
|
+
self.message_count += 1
|
|
71
|
+
|
|
72
|
+
def add_tag(self, tag: str) -> None:
|
|
73
|
+
"""Add a tag to the session."""
|
|
74
|
+
if tag not in self.tags:
|
|
75
|
+
self.tags.append(tag)
|
|
76
|
+
|
|
77
|
+
def set_metadata(self, key: str, value: Any) -> None:
|
|
78
|
+
"""Set a metadata field."""
|
|
79
|
+
self.metadata[key] = value
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SessionManager:
|
|
83
|
+
"""
|
|
84
|
+
Manages conversation sessions with persistence support.
|
|
85
|
+
|
|
86
|
+
Usage::
|
|
87
|
+
# Create manager
|
|
88
|
+
manager = SessionManager()
|
|
89
|
+
|
|
90
|
+
# Create new session
|
|
91
|
+
session = manager.create_session(user_id="user-123")
|
|
92
|
+
print(session.conversation_id)
|
|
93
|
+
|
|
94
|
+
# Get existing session
|
|
95
|
+
session = manager.get_session("conv-123")
|
|
96
|
+
|
|
97
|
+
# Update session activity
|
|
98
|
+
manager.touch("conv-123")
|
|
99
|
+
|
|
100
|
+
# Serialize all sessions
|
|
101
|
+
data = manager.to_dict()
|
|
102
|
+
|
|
103
|
+
# Load from serialized data
|
|
104
|
+
manager2 = SessionManager.from_dict(data)
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self):
|
|
108
|
+
self._sessions: Dict[str, SessionState] = {}
|
|
109
|
+
|
|
110
|
+
def create_session(
|
|
111
|
+
self,
|
|
112
|
+
user_id: Optional[str] = None,
|
|
113
|
+
conversation_id: Optional[str] = None,
|
|
114
|
+
tags: Optional[List[str]] = None,
|
|
115
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
116
|
+
) -> SessionState:
|
|
117
|
+
"""
|
|
118
|
+
Create a new session.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
user_id: Optional user identifier
|
|
122
|
+
conversation_id: Optional explicit conversation_id (auto-generated if not provided)
|
|
123
|
+
tags: Optional list of tags
|
|
124
|
+
metadata: Optional custom metadata
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
New SessionState object
|
|
128
|
+
"""
|
|
129
|
+
cid = conversation_id or str(uuid.uuid4())
|
|
130
|
+
|
|
131
|
+
session = SessionState(
|
|
132
|
+
conversation_id=cid,
|
|
133
|
+
user_id=user_id,
|
|
134
|
+
tags=tags or [],
|
|
135
|
+
metadata=metadata or {},
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self._sessions[cid] = session
|
|
139
|
+
return session
|
|
140
|
+
|
|
141
|
+
def get_session(self, conversation_id: str) -> Optional[SessionState]:
|
|
142
|
+
"""
|
|
143
|
+
Get an existing session by ID.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
conversation_id: The conversation ID to look up
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
SessionState if found, None otherwise
|
|
150
|
+
"""
|
|
151
|
+
return self._sessions.get(conversation_id)
|
|
152
|
+
|
|
153
|
+
def get_or_create_session(
|
|
154
|
+
self,
|
|
155
|
+
conversation_id: str,
|
|
156
|
+
user_id: Optional[str] = None,
|
|
157
|
+
) -> SessionState:
|
|
158
|
+
"""
|
|
159
|
+
Get existing session or create new one if not exists.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
conversation_id: The conversation ID
|
|
163
|
+
user_id: Optional user_id for new session
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Existing or new SessionState
|
|
167
|
+
"""
|
|
168
|
+
if conversation_id in self._sessions:
|
|
169
|
+
return self._sessions[conversation_id]
|
|
170
|
+
|
|
171
|
+
return self.create_session(
|
|
172
|
+
conversation_id=conversation_id,
|
|
173
|
+
user_id=user_id,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def touch(self, conversation_id: str) -> None:
|
|
177
|
+
"""
|
|
178
|
+
Update session activity timestamp and message count.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
conversation_id: The conversation ID to touch
|
|
182
|
+
"""
|
|
183
|
+
session = self._sessions.get(conversation_id)
|
|
184
|
+
if session:
|
|
185
|
+
session.touch()
|
|
186
|
+
|
|
187
|
+
def delete_session(self, conversation_id: str) -> bool:
|
|
188
|
+
"""
|
|
189
|
+
Delete a session.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
conversation_id: The conversation ID to delete
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
True if deleted, False if not found
|
|
196
|
+
"""
|
|
197
|
+
if conversation_id in self._sessions:
|
|
198
|
+
del self._sessions[conversation_id]
|
|
199
|
+
return True
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
def list_sessions(
|
|
203
|
+
self,
|
|
204
|
+
user_id: Optional[str] = None,
|
|
205
|
+
tags: Optional[List[str]] = None,
|
|
206
|
+
limit: Optional[int] = None,
|
|
207
|
+
) -> List[SessionState]:
|
|
208
|
+
"""
|
|
209
|
+
List sessions with optional filters.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
user_id: Filter by user_id
|
|
213
|
+
tags: Filter by tags (must have all specified tags)
|
|
214
|
+
limit: Maximum number of sessions to return
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
List of matching SessionState objects
|
|
218
|
+
"""
|
|
219
|
+
sessions = list(self._sessions.values())
|
|
220
|
+
|
|
221
|
+
if user_id:
|
|
222
|
+
sessions = [s for s in sessions if s.user_id == user_id]
|
|
223
|
+
|
|
224
|
+
if tags:
|
|
225
|
+
sessions = [s for s in sessions if all(tag in s.tags for tag in tags)]
|
|
226
|
+
|
|
227
|
+
# Sort by updated_at (most recent first)
|
|
228
|
+
sessions.sort(key=lambda s: s.updated_at, reverse=True)
|
|
229
|
+
|
|
230
|
+
if limit:
|
|
231
|
+
sessions = sessions[:limit]
|
|
232
|
+
|
|
233
|
+
return sessions
|
|
234
|
+
|
|
235
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
236
|
+
"""Serialize all sessions to dictionary."""
|
|
237
|
+
return {
|
|
238
|
+
"sessions": {
|
|
239
|
+
cid: session.to_dict()
|
|
240
|
+
for cid, session in self._sessions.items()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
def from_dict(cls, data: Dict[str, Any]) -> SessionManager:
|
|
246
|
+
"""Deserialize SessionManager from dictionary."""
|
|
247
|
+
manager = cls()
|
|
248
|
+
|
|
249
|
+
sessions_data = data.get("sessions", {})
|
|
250
|
+
for cid, session_data in sessions_data.items():
|
|
251
|
+
manager._sessions[cid] = SessionState.from_dict(session_data)
|
|
252
|
+
|
|
253
|
+
return manager
|
|
254
|
+
|
|
255
|
+
def __len__(self) -> int:
|
|
256
|
+
"""Return number of active sessions."""
|
|
257
|
+
return len(self._sessions)
|
|
258
|
+
|
|
259
|
+
def __contains__(self, conversation_id: str) -> bool:
|
|
260
|
+
"""Check if conversation_id exists."""
|
|
261
|
+
return conversation_id in self._sessions
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
# Global session manager (optional convenience)
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
_default_manager: Optional[SessionManager] = None
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def get_session_manager() -> SessionManager:
|
|
272
|
+
"""Get or create the default session manager."""
|
|
273
|
+
global _default_manager
|
|
274
|
+
if _default_manager is None:
|
|
275
|
+
_default_manager = SessionManager()
|
|
276
|
+
return _default_manager
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def create_session(
|
|
280
|
+
user_id: Optional[str] = None,
|
|
281
|
+
conversation_id: Optional[str] = None,
|
|
282
|
+
tags: Optional[List[str]] = None,
|
|
283
|
+
) -> SessionState:
|
|
284
|
+
"""
|
|
285
|
+
Create a new session using the default manager.
|
|
286
|
+
|
|
287
|
+
Usage::
|
|
288
|
+
session = gateforge.create_session(user_id="user-123")
|
|
289
|
+
print(session.conversation_id)
|
|
290
|
+
"""
|
|
291
|
+
return get_session_manager().create_session(
|
|
292
|
+
user_id=user_id,
|
|
293
|
+
conversation_id=conversation_id,
|
|
294
|
+
tags=tags,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def get_session(conversation_id: str) -> Optional[SessionState]:
|
|
299
|
+
"""
|
|
300
|
+
Get an existing session using the default manager.
|
|
301
|
+
|
|
302
|
+
Usage::
|
|
303
|
+
session = gateforge.get_session("conv-123")
|
|
304
|
+
if session:
|
|
305
|
+
print(f"Messages: {session.message_count}")
|
|
306
|
+
"""
|
|
307
|
+
return get_session_manager().get_session(conversation_id)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def get_current_conversation_id() -> Optional[str]:
|
|
311
|
+
"""
|
|
312
|
+
Get the conversation_id from the active trace context.
|
|
313
|
+
|
|
314
|
+
Usage::
|
|
315
|
+
cid = gateforge.get_current_conversation_id()
|
|
316
|
+
if cid:
|
|
317
|
+
print(f"Current conversation: {cid}")
|
|
318
|
+
"""
|
|
319
|
+
from gateforge.context import get_active_trace
|
|
320
|
+
trace = get_active_trace()
|
|
321
|
+
return trace.conversation_id if trace else None
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def get_current_trace_info() -> Optional[Dict[str, Any]]:
|
|
325
|
+
"""
|
|
326
|
+
Get information about the current trace context.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Dictionary with conversation_id, trace_id, step, or None if no active trace
|
|
330
|
+
|
|
331
|
+
Usage::
|
|
332
|
+
info = gateforge.get_current_trace_info()
|
|
333
|
+
if info:
|
|
334
|
+
print(f"Conversation: {info['conversation_id']}, Step: {info['step']}")
|
|
335
|
+
"""
|
|
336
|
+
from gateforge.context import get_active_trace
|
|
337
|
+
trace = get_active_trace()
|
|
338
|
+
if trace:
|
|
339
|
+
return {
|
|
340
|
+
"conversation_id": trace.conversation_id,
|
|
341
|
+
"trace_id": trace.trace_id,
|
|
342
|
+
"step": trace.step,
|
|
343
|
+
}
|
|
344
|
+
return None
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gateforge-sdk"
|
|
7
|
-
version = "0.2.
|
|
8
|
-
description = "Privacy-first LLMOps SDK — Auto-init, tool/agent decorators, session
|
|
7
|
+
version = "0.2.5"
|
|
8
|
+
description = "Privacy-first LLMOps SDK — Auto-init, tool/agent decorators, session management, nested conversation support"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
requires-python = ">=3.10"
|