gateforge-sdk 0.2.9__tar.gz → 0.2.11__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.
Files changed (43) hide show
  1. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/PKG-INFO +1 -1
  2. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/client.py +39 -2
  3. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/context.py +1 -2
  4. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/wrappers/gemini.py +2 -0
  5. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/wrappers/openai.py +2 -0
  6. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/pyproject.toml +1 -1
  7. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/.env.example +0 -0
  8. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/.gitignore +0 -0
  9. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/.pypirc.example +0 -0
  10. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/INSTALL.md +0 -0
  11. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/LICENSE +0 -0
  12. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/MANIFEST.in +0 -0
  13. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/PUBLISHING.md +0 -0
  14. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/PUBLISH_NOW.md +0 -0
  15. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/README.md +0 -0
  16. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/__init__.py +0 -0
  17. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/ab/__init__.py +0 -0
  18. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/ab/engine.py +0 -0
  19. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/config.py +0 -0
  20. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/features/__init__.py +0 -0
  21. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/guardrails/__init__.py +0 -0
  22. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/guardrails/engine.py +0 -0
  23. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/metrics.py +0 -0
  24. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/options.py +0 -0
  25. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/pii.py +0 -0
  26. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/pricing.py +0 -0
  27. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/prompt.py +0 -0
  28. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/providers/__init__.py +0 -0
  29. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/providers/anthropic.py +0 -0
  30. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/providers/gemini.py +0 -0
  31. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/providers/openai.py +0 -0
  32. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/response.py +0 -0
  33. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/session_manager.py +0 -0
  34. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/tracing.py +0 -0
  35. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/wrappers/__init__.py +0 -0
  36. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/gateforge/wrappers/anthropic.py +0 -0
  37. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/__init__.py +0 -0
  38. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/test_metrics.py +0 -0
  39. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/test_phase1.py +0 -0
  40. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/test_phase2.py +0 -0
  41. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/test_phase3.py +0 -0
  42. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/test_pii.py +0 -0
  43. {gateforge_sdk-0.2.9 → gateforge_sdk-0.2.11}/tests/test_providers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gateforge-sdk
3
- Version: 0.2.9
3
+ Version: 0.2.11
4
4
  Summary: Privacy-first LLMOps SDK — Auto-init, decorators, session management, prompt system with cache
5
5
  Project-URL: Homepage, https://gateforge.dev
6
6
  Project-URL: Documentation, https://gateforge.dev/docs
@@ -189,6 +189,8 @@ class GatforgeClient:
189
189
  max_tokens: int | None = None,
190
190
  temperature: float | None = None,
191
191
  options: CallOptions | None = None,
192
+ messages: list | None = None,
193
+ system_instruction: str | None = None,
192
194
  ) -> tuple[str, float]:
193
195
  """
194
196
  Output guardrail evaluation + PII rehydration + cost calculation +
@@ -206,8 +208,13 @@ class GatforgeClient:
206
208
  apply_on_fail(output_violations)
207
209
  self._report_violations(output_violations, pii_context.get("thread_id"))
208
210
 
209
- # PII rehydration
210
- rehydrated = rehydrate_text(content, domain, backend, pii_context) if flags.pii_enabled else content
211
+ # PII rehydration — only if something was actually anonymized (non-empty entities)
212
+ # Calling rehydrate with an empty context dict raises "Missing context keys"
213
+ rehydrated = (
214
+ rehydrate_text(content, domain, backend, pii_context)
215
+ if flags.pii_enabled and pii_entities
216
+ else content
217
+ )
211
218
 
212
219
  # Cost
213
220
  cost_usd = calculate_cost(model, prompt_tokens, completion_tokens, provider) if flags.track_cost else 0.0
@@ -243,6 +250,34 @@ class GatforgeClient:
243
250
 
244
251
  # Agentic trace event (richer, waterfall-ready)
245
252
  if flags.tracing_enabled and conversation_id:
253
+ # Extract system prompt: OpenAI/Anthropic messages list or Gemini system_instruction
254
+ sys_prompt: str | None = None
255
+ if system_instruction:
256
+ sys_prompt = str(system_instruction)[:500]
257
+ elif messages:
258
+ for m in messages:
259
+ if not isinstance(m, dict):
260
+ continue
261
+ if m.get("role") == "system":
262
+ c = m.get("content", "")
263
+ sys_prompt = (c if isinstance(c, str) else
264
+ next((p.get("text", "") for p in c if isinstance(p, dict) and p.get("type") == "text"), "")
265
+ )[:500]
266
+ break
267
+
268
+ llm_meta: dict = {}
269
+ if temperature is not None:
270
+ llm_meta["temperature"] = temperature
271
+ if max_tokens is not None:
272
+ llm_meta["max_tokens"] = max_tokens
273
+ if sys_prompt:
274
+ llm_meta["system_prompt"] = sys_prompt
275
+ if messages:
276
+ # Count only non-system turns for a clean "context length" number
277
+ llm_meta["messages_count"] = sum(1 for m in messages if isinstance(m, dict) and m.get("role") != "system")
278
+ if finish_reason:
279
+ llm_meta["finish_reason"] = finish_reason
280
+
246
281
  emit_trace_event(
247
282
  api_key=self.api_key,
248
283
  base_url=self.base_url,
@@ -261,6 +296,7 @@ class GatforgeClient:
261
296
  experiment_id=ab_assignment.experiment_id if ab_assignment else None,
262
297
  variant=ab_assignment.variant if ab_assignment else None,
263
298
  content_preview=rehydrated[:500] if rehydrated else None,
299
+ metadata=llm_meta or None,
264
300
  )
265
301
 
266
302
  return rehydrated, cost_usd
@@ -466,6 +502,7 @@ class GatforgeClient:
466
502
  finish_reason=finish_reason,
467
503
  max_tokens=kwargs.get("max_tokens"),
468
504
  temperature=kwargs.get("temperature"),
505
+ messages=anon_messages,
469
506
  )
470
507
 
471
508
  return GatforgeResponse(
@@ -227,7 +227,7 @@ def agent(
227
227
  event_type="agent_start",
228
228
  step=start_step,
229
229
  content_preview=user_input[:500] if user_input else None,
230
- metadata={"user_id": user_id, "args": _safe_str(args)[:200], "agent_name": name or fn.__name__, "nested": is_nested},
230
+ metadata={"agent_name": name or fn.__name__},
231
231
  )
232
232
 
233
233
  # Enter trace context (only if not already in one)
@@ -248,7 +248,6 @@ def agent(
248
248
  event_type="agent_end",
249
249
  step=active_trace.step if active_trace else ctx.ctx.step,
250
250
  content_preview=result_preview,
251
- metadata={"success": True},
252
251
  )
253
252
 
254
253
  return result
@@ -175,6 +175,8 @@ class _WrappedModels:
175
175
  completion_tokens=getattr(usage, "candidates_token_count", 0) or 0,
176
176
  latency_ms=latency_ms, pii_entities=pii_entities, pii_context=pii_ctx,
177
177
  ab_assignment=ab_assignment, options=options,
178
+ messages=anon_contents,
179
+ system_instruction=str(kwargs["system_instruction"])[:500] if kwargs.get("system_instruction") else None,
178
180
  )
179
181
  if content != rehydrated:
180
182
  _patch_text(response, rehydrated)
@@ -94,6 +94,7 @@ class _WrappedCompletions:
94
94
  ab_assignment=ab_assignment,
95
95
  finish_reason=finish_reason,
96
96
  max_tokens=kwargs.get("max_tokens"), temperature=kwargs.get("temperature"),
97
+ messages=anon_messages,
97
98
  options=options,
98
99
  )
99
100
  if response.choices[0].message.content != rehydrated:
@@ -225,6 +226,7 @@ class _WrappedAsyncCompletions:
225
226
  ab_assignment=ab_assignment,
226
227
  finish_reason=finish_reason,
227
228
  max_tokens=kwargs.get("max_tokens"), temperature=kwargs.get("temperature"),
229
+ messages=anon_messages,
228
230
  options=options,
229
231
  )
230
232
  if response.choices[0].message.content != rehydrated:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "gateforge-sdk"
7
- version = "0.2.9"
7
+ version = "0.2.11"
8
8
  description = "Privacy-first LLMOps SDK — Auto-init, decorators, session management, prompt system with cache"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
File without changes
File without changes
File without changes
File without changes