tracia 0.1.1__tar.gz → 0.2.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.
Files changed (27) hide show
  1. {tracia-0.1.1 → tracia-0.2.0}/PKG-INFO +1 -1
  2. {tracia-0.1.1 → tracia-0.2.0}/pyproject.toml +1 -1
  3. {tracia-0.1.1 → tracia-0.2.0}/tracia/__init__.py +3 -0
  4. {tracia-0.1.1 → tracia-0.2.0}/tracia/_client.py +24 -1
  5. {tracia-0.1.1 → tracia-0.2.0}/tracia/_constants.py +1 -1
  6. {tracia-0.1.1 → tracia-0.2.0}/tracia/_llm.py +56 -0
  7. {tracia-0.1.1 → tracia-0.2.0}/tracia/_types.py +21 -0
  8. {tracia-0.1.1 → tracia-0.2.0}/.claude/settings.local.json +0 -0
  9. {tracia-0.1.1 → tracia-0.2.0}/.gitignore +0 -0
  10. {tracia-0.1.1 → tracia-0.2.0}/CLAUDE.md +0 -0
  11. {tracia-0.1.1 → tracia-0.2.0}/LICENSE +0 -0
  12. {tracia-0.1.1 → tracia-0.2.0}/README.md +0 -0
  13. {tracia-0.1.1 → tracia-0.2.0}/tests/__init__.py +0 -0
  14. {tracia-0.1.1 → tracia-0.2.0}/tests/test_client.py +0 -0
  15. {tracia-0.1.1 → tracia-0.2.0}/tests/test_errors.py +0 -0
  16. {tracia-0.1.1 → tracia-0.2.0}/tests/test_llm.py +0 -0
  17. {tracia-0.1.1 → tracia-0.2.0}/tests/test_types.py +0 -0
  18. {tracia-0.1.1 → tracia-0.2.0}/tests/test_utils.py +0 -0
  19. {tracia-0.1.1 → tracia-0.2.0}/tracia/_errors.py +0 -0
  20. {tracia-0.1.1 → tracia-0.2.0}/tracia/_http.py +0 -0
  21. {tracia-0.1.1 → tracia-0.2.0}/tracia/_session.py +0 -0
  22. {tracia-0.1.1 → tracia-0.2.0}/tracia/_streaming.py +0 -0
  23. {tracia-0.1.1 → tracia-0.2.0}/tracia/_utils.py +0 -0
  24. {tracia-0.1.1 → tracia-0.2.0}/tracia/py.typed +0 -0
  25. {tracia-0.1.1 → tracia-0.2.0}/tracia/resources/__init__.py +0 -0
  26. {tracia-0.1.1 → tracia-0.2.0}/tracia/resources/prompts.py +0 -0
  27. {tracia-0.1.1 → tracia-0.2.0}/tracia/resources/spans.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tracia
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: LLM prompt management and tracing SDK
5
5
  Project-URL: Homepage, https://tracia.io
6
6
  Project-URL: Documentation, https://docs.tracia.io
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tracia"
7
- version = "0.1.1"
7
+ version = "0.2.0"
8
8
  description = "LLM prompt management and tracing SDK"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -66,6 +66,7 @@ from ._types import (
66
66
  PromptListItem,
67
67
  PromptMessage,
68
68
  PromptVersion,
69
+ ResponseFormatJsonSchema,
69
70
  ResponsesEvent,
70
71
  ResponsesFunctionCall,
71
72
  ResponsesFunctionCallOutput,
@@ -121,6 +122,8 @@ __all__ = [
121
122
  "JsonSchemaProperty",
122
123
  "ToolCall",
123
124
  "ToolChoice",
125
+ # Types - Response Format
126
+ "ResponseFormatJsonSchema",
124
127
  # Types - Run Local
125
128
  "RunLocalInput",
126
129
  "RunLocalResult",
@@ -20,13 +20,14 @@ from ._constants import (
20
20
  )
21
21
  from ._errors import TraciaError, TraciaErrorCode, sanitize_error_message
22
22
  from ._http import AsyncHttpClient, HttpClient
23
- from ._llm import LLMClient, build_assistant_message, resolve_provider
23
+ from ._llm import LLMClient, build_assistant_message, convert_response_format, resolve_provider
24
24
  from ._session import TraciaSession
25
25
  from ._streaming import AsyncLocalStream, LocalStream
26
26
  from ._types import (
27
27
  CreateSpanPayload,
28
28
  LocalPromptMessage,
29
29
  LLMProvider,
30
+ ResponseFormat,
30
31
  RunLocalResult,
31
32
  StreamResult,
32
33
  TokenUsage,
@@ -263,6 +264,7 @@ class Tracia:
263
264
  span_id: str | None = None,
264
265
  tools: list[ToolDefinition] | None = None,
265
266
  tool_choice: ToolChoice | None = None,
267
+ response_format: ResponseFormat | None = None,
266
268
  trace_id: str | None = None,
267
269
  parent_span_id: str | None = None,
268
270
  ) -> RunLocalResult: ...
@@ -289,6 +291,7 @@ class Tracia:
289
291
  span_id: str | None = None,
290
292
  tools: list[ToolDefinition] | None = None,
291
293
  tool_choice: ToolChoice | None = None,
294
+ response_format: ResponseFormat | None = None,
292
295
  trace_id: str | None = None,
293
296
  parent_span_id: str | None = None,
294
297
  ) -> LocalStream: ...
@@ -314,6 +317,7 @@ class Tracia:
314
317
  span_id: str | None = None,
315
318
  tools: list[ToolDefinition] | None = None,
316
319
  tool_choice: ToolChoice | None = None,
320
+ response_format: ResponseFormat | None = None,
317
321
  trace_id: str | None = None,
318
322
  parent_span_id: str | None = None,
319
323
  ) -> RunLocalResult | LocalStream:
@@ -361,6 +365,8 @@ class Tracia:
361
365
  # Calculate timeout
362
366
  timeout_seconds = (timeout_ms or DEFAULT_TIMEOUT_MS) / 1000.0
363
367
 
368
+ litellm_response_format = convert_response_format(response_format)
369
+
364
370
  if stream:
365
371
  return self._run_local_streaming(
366
372
  messages=prompt_messages,
@@ -382,6 +388,7 @@ class Tracia:
382
388
  tools=tools,
383
389
  tool_choice=tool_choice,
384
390
  variables=variables,
391
+ response_format=litellm_response_format,
385
392
  )
386
393
  else:
387
394
  return self._run_local_non_streaming(
@@ -404,6 +411,7 @@ class Tracia:
404
411
  tools=tools,
405
412
  tool_choice=tool_choice,
406
413
  variables=variables,
414
+ response_format=litellm_response_format,
407
415
  )
408
416
 
409
417
  def _run_local_non_streaming(
@@ -428,6 +436,7 @@ class Tracia:
428
436
  tools: list[ToolDefinition] | None,
429
437
  tool_choice: ToolChoice | None,
430
438
  variables: dict[str, str] | None,
439
+ response_format: dict[str, Any] | None = None,
431
440
  ) -> RunLocalResult:
432
441
  """Run local prompt without streaming."""
433
442
  start_time = time.time()
@@ -451,6 +460,7 @@ class Tracia:
451
460
  tool_choice=tool_choice,
452
461
  api_key=provider_api_key,
453
462
  timeout=timeout,
463
+ response_format=response_format,
454
464
  )
455
465
 
456
466
  result_text = completion.text
@@ -537,6 +547,7 @@ class Tracia:
537
547
  tools: list[ToolDefinition] | None,
538
548
  tool_choice: ToolChoice | None,
539
549
  variables: dict[str, str] | None,
550
+ response_format: dict[str, Any] | None = None,
540
551
  ) -> LocalStream:
541
552
  """Run local prompt with streaming."""
542
553
  start_time = time.time()
@@ -555,6 +566,7 @@ class Tracia:
555
566
  tool_choice=tool_choice,
556
567
  api_key=provider_api_key,
557
568
  timeout=timeout,
569
+ response_format=response_format,
558
570
  )
559
571
 
560
572
  def wrapped_chunks():
@@ -660,6 +672,7 @@ class Tracia:
660
672
  span_id: str | None = None,
661
673
  tools: list[ToolDefinition] | None = None,
662
674
  tool_choice: ToolChoice | None = None,
675
+ response_format: ResponseFormat | None = None,
663
676
  trace_id: str | None = None,
664
677
  parent_span_id: str | None = None,
665
678
  ) -> RunLocalResult: ...
@@ -686,6 +699,7 @@ class Tracia:
686
699
  span_id: str | None = None,
687
700
  tools: list[ToolDefinition] | None = None,
688
701
  tool_choice: ToolChoice | None = None,
702
+ response_format: ResponseFormat | None = None,
689
703
  trace_id: str | None = None,
690
704
  parent_span_id: str | None = None,
691
705
  ) -> AsyncLocalStream: ...
@@ -711,6 +725,7 @@ class Tracia:
711
725
  span_id: str | None = None,
712
726
  tools: list[ToolDefinition] | None = None,
713
727
  tool_choice: ToolChoice | None = None,
728
+ response_format: ResponseFormat | None = None,
714
729
  trace_id: str | None = None,
715
730
  parent_span_id: str | None = None,
716
731
  ) -> RunLocalResult | AsyncLocalStream:
@@ -732,6 +747,8 @@ class Tracia:
732
747
  # Calculate timeout
733
748
  timeout_seconds = (timeout_ms or DEFAULT_TIMEOUT_MS) / 1000.0
734
749
 
750
+ litellm_response_format = convert_response_format(response_format)
751
+
735
752
  if stream:
736
753
  return await self._arun_local_streaming(
737
754
  messages=prompt_messages,
@@ -753,6 +770,7 @@ class Tracia:
753
770
  tools=tools,
754
771
  tool_choice=tool_choice,
755
772
  variables=variables,
773
+ response_format=litellm_response_format,
756
774
  )
757
775
  else:
758
776
  return await self._arun_local_non_streaming(
@@ -775,6 +793,7 @@ class Tracia:
775
793
  tools=tools,
776
794
  tool_choice=tool_choice,
777
795
  variables=variables,
796
+ response_format=litellm_response_format,
778
797
  )
779
798
 
780
799
  async def _arun_local_non_streaming(
@@ -799,6 +818,7 @@ class Tracia:
799
818
  tools: list[ToolDefinition] | None,
800
819
  tool_choice: ToolChoice | None,
801
820
  variables: dict[str, str] | None,
821
+ response_format: dict[str, Any] | None = None,
802
822
  ) -> RunLocalResult:
803
823
  """Run local prompt without streaming (async)."""
804
824
  start_time = time.time()
@@ -823,6 +843,7 @@ class Tracia:
823
843
  tool_choice=tool_choice,
824
844
  api_key=provider_api_key,
825
845
  timeout=timeout,
846
+ response_format=response_format,
826
847
  )
827
848
 
828
849
  result_text = completion.text
@@ -909,6 +930,7 @@ class Tracia:
909
930
  tools: list[ToolDefinition] | None,
910
931
  tool_choice: ToolChoice | None,
911
932
  variables: dict[str, str] | None,
933
+ response_format: dict[str, Any] | None = None,
912
934
  ) -> AsyncLocalStream:
913
935
  """Run local prompt with streaming (async)."""
914
936
  start_time = time.time()
@@ -928,6 +950,7 @@ class Tracia:
928
950
  tool_choice=tool_choice,
929
951
  api_key=provider_api_key,
930
952
  timeout=timeout,
953
+ response_format=response_format,
931
954
  )
932
955
 
933
956
  async def wrapped_chunks():
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  # SDK Version (defined here to avoid circular imports)
6
- SDK_VERSION = "0.1.1"
6
+ SDK_VERSION = "0.2.0"
7
7
 
8
8
  # API Configuration
9
9
  BASE_URL = "https://app.tracia.io"
@@ -14,6 +14,7 @@ from ._types import (
14
14
  FinishReason,
15
15
  LLMProvider,
16
16
  LocalPromptMessage,
17
+ ResponseFormatJsonSchema,
17
18
  TextPart,
18
19
  ToolCall,
19
20
  ToolCallPart,
@@ -284,6 +285,49 @@ def convert_tool_choice(tool_choice: ToolChoice | None) -> str | dict[str, Any]
284
285
  return None
285
286
 
286
287
 
288
+ def convert_response_format(
289
+ response_format: dict[str, Any] | ResponseFormatJsonSchema | None,
290
+ ) -> dict[str, Any] | None:
291
+ """Convert a Tracia response format to LiteLLM format.
292
+
293
+ Args:
294
+ response_format: The Tracia response format.
295
+
296
+ Returns:
297
+ Response format in LiteLLM/OpenAI format, or None.
298
+ """
299
+ if response_format is None:
300
+ return None
301
+
302
+ if isinstance(response_format, ResponseFormatJsonSchema):
303
+ schema = response_format.schema_
304
+ name = response_format.name or "response"
305
+ json_schema: dict[str, Any] = {"name": name, "schema": schema}
306
+ if response_format.description is not None:
307
+ json_schema["description"] = response_format.description
308
+ return {"type": "json_schema", "json_schema": json_schema}
309
+
310
+ # Plain dict — check for our simplified format
311
+ if isinstance(response_format, dict):
312
+ fmt_type = response_format.get("type")
313
+ schema = response_format.get("schema")
314
+
315
+ if fmt_type == "json" and schema is not None:
316
+ name = response_format.get("name", "response")
317
+ json_schema: dict[str, Any] = {"name": name, "schema": schema}
318
+ description = response_format.get("description")
319
+ if description is not None:
320
+ json_schema["description"] = description
321
+ return {"type": "json_schema", "json_schema": json_schema}
322
+
323
+ if fmt_type == "json":
324
+ return {"type": "json_object"}
325
+
326
+ return response_format
327
+
328
+ return None
329
+
330
+
287
331
  def parse_finish_reason(reason: str | None) -> FinishReason:
288
332
  """Parse the finish reason from LiteLLM response.
289
333
 
@@ -357,6 +401,7 @@ class LLMClient:
357
401
  tool_choice: ToolChoice | None = None,
358
402
  api_key: str | None = None,
359
403
  timeout: float | None = None,
404
+ response_format: dict[str, Any] | None = None,
360
405
  ) -> CompletionResult:
361
406
  """Make a synchronous completion request.
362
407
 
@@ -415,6 +460,8 @@ class LLMClient:
415
460
  request_kwargs["tool_choice"] = litellm_tool_choice
416
461
  if timeout is not None:
417
462
  request_kwargs["timeout"] = timeout
463
+ if response_format is not None:
464
+ request_kwargs["response_format"] = response_format
418
465
 
419
466
  try:
420
467
  response = litellm.completion(**request_kwargs)
@@ -456,6 +503,7 @@ class LLMClient:
456
503
  tool_choice: ToolChoice | None = None,
457
504
  api_key: str | None = None,
458
505
  timeout: float | None = None,
506
+ response_format: dict[str, Any] | None = None,
459
507
  ) -> CompletionResult:
460
508
  """Make an asynchronous completion request.
461
509
 
@@ -514,6 +562,8 @@ class LLMClient:
514
562
  request_kwargs["tool_choice"] = litellm_tool_choice
515
563
  if timeout is not None:
516
564
  request_kwargs["timeout"] = timeout
565
+ if response_format is not None:
566
+ request_kwargs["response_format"] = response_format
517
567
 
518
568
  try:
519
569
  response = await litellm.acompletion(**request_kwargs)
@@ -555,6 +605,7 @@ class LLMClient:
555
605
  tool_choice: ToolChoice | None = None,
556
606
  api_key: str | None = None,
557
607
  timeout: float | None = None,
608
+ response_format: dict[str, Any] | None = None,
558
609
  ) -> tuple[Iterator[str], list[CompletionResult], LLMProvider]:
559
610
  """Make a streaming completion request.
560
611
 
@@ -614,6 +665,8 @@ class LLMClient:
614
665
  request_kwargs["tool_choice"] = litellm_tool_choice
615
666
  if timeout is not None:
616
667
  request_kwargs["timeout"] = timeout
668
+ if response_format is not None:
669
+ request_kwargs["response_format"] = response_format
617
670
 
618
671
  result_holder: list[CompletionResult] = []
619
672
 
@@ -717,6 +770,7 @@ class LLMClient:
717
770
  tool_choice: ToolChoice | None = None,
718
771
  api_key: str | None = None,
719
772
  timeout: float | None = None,
773
+ response_format: dict[str, Any] | None = None,
720
774
  ) -> tuple[AsyncIterator[str], list[CompletionResult], LLMProvider]:
721
775
  """Make an async streaming completion request.
722
776
 
@@ -776,6 +830,8 @@ class LLMClient:
776
830
  request_kwargs["tool_choice"] = litellm_tool_choice
777
831
  if timeout is not None:
778
832
  request_kwargs["timeout"] = timeout
833
+ if response_format is not None:
834
+ request_kwargs["response_format"] = response_format
779
835
 
780
836
  result_holder: list[CompletionResult] = []
781
837
 
@@ -90,6 +90,26 @@ class ToolCall(BaseModel):
90
90
  ToolChoice = Union[Literal["auto", "none", "required"], dict[str, str]]
91
91
 
92
92
 
93
+ # Response Format
94
+
95
+
96
+ class ResponseFormatJsonSchema(BaseModel):
97
+ """JSON schema response format for structured outputs."""
98
+
99
+ model_config = ConfigDict(populate_by_name=True)
100
+
101
+ type: Literal["json"] = "json"
102
+ schema_: dict[str, Any] = Field(alias="schema")
103
+ name: str | None = None
104
+ description: str | None = None
105
+
106
+
107
+ ResponseFormat = Union[
108
+ dict[str, Any],
109
+ ResponseFormatJsonSchema,
110
+ ]
111
+
112
+
93
113
  # Messages
94
114
 
95
115
 
@@ -134,6 +154,7 @@ class RunLocalInput(BaseModel):
134
154
  span_id: str | None = Field(default=None, alias="spanId")
135
155
  tools: list[ToolDefinition] | None = None
136
156
  tool_choice: ToolChoice | None = Field(default=None, alias="toolChoice")
157
+ response_format: ResponseFormat | None = Field(default=None, alias="responseFormat")
137
158
  trace_id: str | None = Field(default=None, alias="traceId")
138
159
  parent_span_id: str | None = Field(default=None, alias="parentSpanId")
139
160
 
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