gateforge-sdk 0.2.3__tar.gz → 0.2.4__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.4}/PKG-INFO +2 -2
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/context.py +29 -9
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/pyproject.toml +2 -2
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/tests/test_phase1.py +57 -31
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/.env.example +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/.gitignore +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/.pypirc.example +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/INSTALL.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/LICENSE +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/MANIFEST.in +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/PUBLISHING.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/PUBLISH_NOW.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/README.md +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/ab/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/ab/engine.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/client.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/config.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/features/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/guardrails/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/guardrails/engine.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/metrics.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/options.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/pii.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/pricing.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/prompt.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/providers/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/providers/anthropic.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/providers/gemini.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/providers/openai.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/response.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/tracing.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/wrappers/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/wrappers/anthropic.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/wrappers/gemini.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/gateforge/wrappers/openai.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/tests/__init__.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/tests/test_metrics.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/tests/test_pii.py +0 -0
- {gateforge_sdk-0.2.3 → gateforge_sdk-0.2.4}/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 tracing,
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Summary: Privacy-first LLMOps SDK — Auto-init, tool/agent decorators, session tracing, 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
|
|
@@ -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
|
|
@@ -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 tracing,
|
|
7
|
+
version = "0.2.4"
|
|
8
|
+
description = "Privacy-first LLMOps SDK — Auto-init, tool/agent decorators, session tracing, nested conversation support"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -20,9 +20,7 @@ class TestAutoInit:
|
|
|
20
20
|
|
|
21
21
|
def test_auto_init_no_api_key(self, capsys):
|
|
22
22
|
"""auto_init() should silently skip if no API key found."""
|
|
23
|
-
# Ensure no API key in env
|
|
24
23
|
with patch.dict(os.environ, {}, clear=True):
|
|
25
|
-
# Need to reload module to pick up new env
|
|
26
24
|
import importlib
|
|
27
25
|
import gateforge
|
|
28
26
|
importlib.reload(gateforge)
|
|
@@ -36,7 +34,6 @@ class TestAutoInit:
|
|
|
36
34
|
def test_auto_init_with_api_key(self, capsys):
|
|
37
35
|
"""auto_init() should initialize successfully with API key."""
|
|
38
36
|
with patch.dict(os.environ, {"GATEFORGE_API_KEY": "gf_test_key"}):
|
|
39
|
-
# Mock the init function to avoid actual API calls
|
|
40
37
|
with patch('gateforge.init') as mock_init:
|
|
41
38
|
import gateforge
|
|
42
39
|
result = gateforge.auto_init()
|
|
@@ -45,7 +42,6 @@ class TestAutoInit:
|
|
|
45
42
|
captured = capsys.readouterr()
|
|
46
43
|
assert "Auto-initialized successfully" in captured.out
|
|
47
44
|
|
|
48
|
-
# Verify init was called with correct params
|
|
49
45
|
mock_init.assert_called_once()
|
|
50
46
|
call_kwargs = mock_init.call_args[1]
|
|
51
47
|
assert call_kwargs['api_key'] == 'gf_test_key'
|
|
@@ -107,7 +103,6 @@ class TestToolDecorator:
|
|
|
107
103
|
"""Tool should emit trace event when inside active trace."""
|
|
108
104
|
import gateforge
|
|
109
105
|
|
|
110
|
-
# Mock emit_trace_event in tracing module where it's called
|
|
111
106
|
with patch('gateforge.tracing.emit_trace_event') as mock_emit:
|
|
112
107
|
@gateforge.tool()
|
|
113
108
|
def weather_tool(location: str) -> str:
|
|
@@ -118,7 +113,6 @@ class TestToolDecorator:
|
|
|
118
113
|
|
|
119
114
|
assert result == "Weather in Madrid: sunny"
|
|
120
115
|
|
|
121
|
-
# Verify trace event was emitted
|
|
122
116
|
mock_emit.assert_called_once()
|
|
123
117
|
call_kwargs = mock_emit.call_args[1]
|
|
124
118
|
assert call_kwargs['conversation_id'] == 'test-123'
|
|
@@ -140,7 +134,6 @@ class TestToolDecorator:
|
|
|
140
134
|
with pytest.raises(ValueError):
|
|
141
135
|
failing_tool()
|
|
142
136
|
|
|
143
|
-
# Verify error was captured
|
|
144
137
|
mock_emit.assert_called_once()
|
|
145
138
|
call_kwargs = mock_emit.call_args[1]
|
|
146
139
|
assert call_kwargs['metadata']['success'] is False
|
|
@@ -185,13 +178,12 @@ class TestAgentDecorator:
|
|
|
185
178
|
"""Tests for @gateforge.agent() decorator."""
|
|
186
179
|
|
|
187
180
|
def test_agent_creates_trace_context(self):
|
|
188
|
-
"""Agent should create trace context
|
|
181
|
+
"""Agent should create trace context if not already in one."""
|
|
189
182
|
import gateforge
|
|
190
183
|
from gateforge.context import get_active_trace
|
|
191
184
|
|
|
192
185
|
@gateforge.agent(conversation_id="agent-test-1")
|
|
193
186
|
def my_agent(message: str) -> str:
|
|
194
|
-
# Should have active trace inside agent
|
|
195
187
|
trace_ctx = get_active_trace()
|
|
196
188
|
assert trace_ctx is not None
|
|
197
189
|
assert trace_ctx.conversation_id == "agent-test-1"
|
|
@@ -200,6 +192,20 @@ class TestAgentDecorator:
|
|
|
200
192
|
result = my_agent("Hello")
|
|
201
193
|
assert "Hello" in result
|
|
202
194
|
|
|
195
|
+
def test_agent_uses_active_session_conversation_id(self):
|
|
196
|
+
"""Agent should use session's conversation_id when nested."""
|
|
197
|
+
import gateforge
|
|
198
|
+
from gateforge.context import get_active_trace
|
|
199
|
+
|
|
200
|
+
@gateforge.agent()
|
|
201
|
+
def my_agent():
|
|
202
|
+
trace_ctx = get_active_trace()
|
|
203
|
+
return trace_ctx.conversation_id
|
|
204
|
+
|
|
205
|
+
with gateforge.session(conversation_id="session-123") as sess:
|
|
206
|
+
agent_cid = my_agent()
|
|
207
|
+
assert agent_cid == "session-123"
|
|
208
|
+
|
|
203
209
|
def test_agent_emits_start_end_events(self):
|
|
204
210
|
"""Agent should emit start and end events."""
|
|
205
211
|
import gateforge
|
|
@@ -211,19 +217,33 @@ class TestAgentDecorator:
|
|
|
211
217
|
|
|
212
218
|
result = my_agent("Hello")
|
|
213
219
|
|
|
214
|
-
# Should emit 2 events: start and end
|
|
215
220
|
assert mock_emit.call_count == 2
|
|
216
221
|
|
|
217
|
-
# First call: agent_start
|
|
218
222
|
first_call = mock_emit.call_args_list[0][1]
|
|
219
223
|
assert first_call['event_type'] == 'agent_start'
|
|
220
224
|
assert first_call['conversation_id'] == 'agent-test-2'
|
|
221
225
|
|
|
222
|
-
# Second call: agent_end
|
|
223
226
|
second_call = mock_emit.call_args_list[1][1]
|
|
224
227
|
assert second_call['event_type'] == 'agent_end'
|
|
225
228
|
assert second_call['metadata']['success'] is True
|
|
226
229
|
|
|
230
|
+
def test_agent_nested_doesnt_create_new_trace(self):
|
|
231
|
+
"""Nested agent should not create new trace context."""
|
|
232
|
+
import gateforge
|
|
233
|
+
|
|
234
|
+
with patch('gateforge.tracing.emit_trace_event') as mock_emit:
|
|
235
|
+
@gateforge.agent()
|
|
236
|
+
def my_agent():
|
|
237
|
+
return "OK"
|
|
238
|
+
|
|
239
|
+
with gateforge.session(conversation_id="session-456"):
|
|
240
|
+
my_agent()
|
|
241
|
+
|
|
242
|
+
assert mock_emit.call_count == 4
|
|
243
|
+
|
|
244
|
+
conversation_ids = [call[1]['conversation_id'] for call in mock_emit.call_args_list]
|
|
245
|
+
assert all(cid == 'session-456' for cid in conversation_ids)
|
|
246
|
+
|
|
227
247
|
def test_agent_emits_error_event(self):
|
|
228
248
|
"""Agent should emit error event on exception."""
|
|
229
249
|
import gateforge
|
|
@@ -236,16 +256,14 @@ class TestAgentDecorator:
|
|
|
236
256
|
with pytest.raises(RuntimeError):
|
|
237
257
|
failing_agent()
|
|
238
258
|
|
|
239
|
-
# Should emit 2 events: start and error
|
|
240
259
|
assert mock_emit.call_count == 2
|
|
241
260
|
|
|
242
|
-
# Second call: agent_error
|
|
243
261
|
second_call = mock_emit.call_args_list[1][1]
|
|
244
262
|
assert second_call['event_type'] == 'agent_error'
|
|
245
263
|
assert 'Agent failed' in second_call['metadata']['error']
|
|
246
264
|
|
|
247
265
|
def test_agent_auto_generates_conversation_id(self):
|
|
248
|
-
"""Agent should auto-generate conversation_id if not provided."""
|
|
266
|
+
"""Agent should auto-generate conversation_id if not provided and no active trace."""
|
|
249
267
|
import gateforge
|
|
250
268
|
|
|
251
269
|
with patch('gateforge.tracing.emit_trace_event') as mock_emit:
|
|
@@ -255,11 +273,27 @@ class TestAgentDecorator:
|
|
|
255
273
|
|
|
256
274
|
my_agent()
|
|
257
275
|
|
|
258
|
-
# Verify conversation_id was generated
|
|
259
276
|
first_call = mock_emit.call_args_list[0][1]
|
|
260
277
|
assert 'conversation_id' in first_call
|
|
261
278
|
assert first_call['conversation_id'].startswith('my_agent-')
|
|
262
279
|
|
|
280
|
+
def test_agent_nested_shows_metadata(self):
|
|
281
|
+
"""Nested agent should include nested=True in metadata."""
|
|
282
|
+
import gateforge
|
|
283
|
+
|
|
284
|
+
with patch('gateforge.tracing.emit_trace_event') as mock_emit:
|
|
285
|
+
@gateforge.agent()
|
|
286
|
+
def my_agent():
|
|
287
|
+
return "OK"
|
|
288
|
+
|
|
289
|
+
with gateforge.session():
|
|
290
|
+
my_agent()
|
|
291
|
+
|
|
292
|
+
for call in mock_emit.call_args_list:
|
|
293
|
+
if call[1]['event_type'] == 'agent_start':
|
|
294
|
+
assert call[1]['metadata']['nested'] is True
|
|
295
|
+
break
|
|
296
|
+
|
|
263
297
|
def test_agent_with_user_id(self):
|
|
264
298
|
"""Agent should include user_id in metadata."""
|
|
265
299
|
import gateforge
|
|
@@ -287,15 +321,12 @@ class TestSession:
|
|
|
287
321
|
assert sess.conversation_id is not None
|
|
288
322
|
assert sess.user_id == "user-456"
|
|
289
323
|
|
|
290
|
-
# Should emit start and end events
|
|
291
324
|
assert mock_emit.call_count == 2
|
|
292
325
|
|
|
293
|
-
# First: session_start
|
|
294
326
|
first_call = mock_emit.call_args_list[0][1]
|
|
295
327
|
assert first_call['event_type'] == 'session_start'
|
|
296
328
|
assert first_call['metadata']['user_id'] == 'user-456'
|
|
297
329
|
|
|
298
|
-
# Second: session_end
|
|
299
330
|
second_call = mock_emit.call_args_list[1][1]
|
|
300
331
|
assert second_call['event_type'] == 'session_end'
|
|
301
332
|
|
|
@@ -314,15 +345,12 @@ class TestSession:
|
|
|
314
345
|
"""Session provides a conversation context that can be used by nested traces."""
|
|
315
346
|
import gateforge
|
|
316
347
|
|
|
317
|
-
# Session provides its conversation_id for use
|
|
318
348
|
with gateforge.session(conversation_id="session-test") as sess:
|
|
319
|
-
# Can explicitly continue the session's conversation
|
|
320
349
|
with gateforge.continue_session(sess.conversation_id) as t1:
|
|
321
350
|
assert t1.conversation_id == "session-test"
|
|
322
351
|
|
|
323
|
-
# Or create independent traces (also valid)
|
|
324
352
|
with gateforge.trace() as t2:
|
|
325
|
-
assert t2.conversation_id != "session-test"
|
|
353
|
+
assert t2.conversation_id != "session-test"
|
|
326
354
|
|
|
327
355
|
def test_session_emits_error_on_exception(self):
|
|
328
356
|
"""Session should emit error event on exception."""
|
|
@@ -333,10 +361,8 @@ class TestSession:
|
|
|
333
361
|
with gateforge.session() as sess:
|
|
334
362
|
raise ValueError("Session error")
|
|
335
363
|
|
|
336
|
-
# Should emit start, error, and finally end (3 total)
|
|
337
364
|
assert mock_emit.call_count >= 2
|
|
338
365
|
|
|
339
|
-
# Find the error event
|
|
340
366
|
error_event = None
|
|
341
367
|
for call in mock_emit.call_args_list:
|
|
342
368
|
if call[1]['event_type'] == 'session_error':
|
|
@@ -351,7 +377,7 @@ class TestIntegration:
|
|
|
351
377
|
"""Integration tests combining multiple features."""
|
|
352
378
|
|
|
353
379
|
def test_agent_with_tools(self):
|
|
354
|
-
"""Agent with decorated tools should trace everything."""
|
|
380
|
+
"""Agent with decorated tools should trace everything with same conversation_id."""
|
|
355
381
|
import gateforge
|
|
356
382
|
|
|
357
383
|
with patch('gateforge.tracing.emit_trace_event') as mock_emit:
|
|
@@ -366,18 +392,16 @@ class TestIntegration:
|
|
|
366
392
|
|
|
367
393
|
result = my_agent()
|
|
368
394
|
|
|
369
|
-
# Should emit: agent_start, tool_call, agent_end
|
|
370
395
|
assert mock_emit.call_count == 3
|
|
371
396
|
|
|
372
397
|
events = [call[1]['event_type'] for call in mock_emit.call_args_list]
|
|
373
398
|
assert events == ['agent_start', 'tool_call', 'agent_end']
|
|
374
399
|
|
|
375
|
-
# All should share same conversation_id
|
|
376
400
|
conversation_ids = [call[1]['conversation_id'] for call in mock_emit.call_args_list]
|
|
377
401
|
assert all(cid == 'integration-1' for cid in conversation_ids)
|
|
378
402
|
|
|
379
403
|
def test_session_with_agent_and_tools(self):
|
|
380
|
-
"""Session wrapping agent with tools should
|
|
404
|
+
"""Session wrapping agent with tools should all share same conversation_id."""
|
|
381
405
|
import gateforge
|
|
382
406
|
|
|
383
407
|
with patch('gateforge.tracing.emit_trace_event') as mock_emit:
|
|
@@ -389,10 +413,9 @@ class TestIntegration:
|
|
|
389
413
|
def weather_agent(location: str):
|
|
390
414
|
return get_weather(location)
|
|
391
415
|
|
|
392
|
-
with gateforge.session(
|
|
416
|
+
with gateforge.session(conversation_id="session-integration"):
|
|
393
417
|
result = weather_agent("Madrid")
|
|
394
418
|
|
|
395
|
-
# Should emit: session_start, agent_start, tool_call, agent_end, session_end
|
|
396
419
|
assert mock_emit.call_count == 5
|
|
397
420
|
|
|
398
421
|
events = [call[1]['event_type'] for call in mock_emit.call_args_list]
|
|
@@ -403,6 +426,9 @@ class TestIntegration:
|
|
|
403
426
|
'agent_end',
|
|
404
427
|
'session_end'
|
|
405
428
|
]
|
|
429
|
+
|
|
430
|
+
conversation_ids = [call[1]['conversation_id'] for call in mock_emit.call_args_list]
|
|
431
|
+
assert all(cid == 'session-integration' for cid in conversation_ids), f"Got different IDs: {set(conversation_ids)}"
|
|
406
432
|
|
|
407
433
|
|
|
408
434
|
if __name__ == '__main__':
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|