braintrust 0.3.14__py3-none-any.whl → 0.4.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.
Files changed (83) hide show
  1. braintrust/__init__.py +4 -0
  2. braintrust/_generated_types.py +1200 -611
  3. braintrust/audit.py +2 -2
  4. braintrust/cli/eval.py +6 -7
  5. braintrust/cli/push.py +11 -11
  6. braintrust/conftest.py +1 -0
  7. braintrust/context.py +12 -17
  8. braintrust/contrib/temporal/__init__.py +16 -27
  9. braintrust/contrib/temporal/test_temporal.py +8 -3
  10. braintrust/devserver/auth.py +8 -8
  11. braintrust/devserver/cache.py +3 -4
  12. braintrust/devserver/cors.py +8 -7
  13. braintrust/devserver/dataset.py +3 -5
  14. braintrust/devserver/eval_hooks.py +7 -6
  15. braintrust/devserver/schemas.py +22 -19
  16. braintrust/devserver/server.py +19 -12
  17. braintrust/devserver/test_cached_login.py +4 -4
  18. braintrust/framework.py +128 -140
  19. braintrust/framework2.py +88 -87
  20. braintrust/functions/invoke.py +93 -53
  21. braintrust/functions/stream.py +3 -2
  22. braintrust/generated_types.py +17 -1
  23. braintrust/git_fields.py +11 -11
  24. braintrust/gitutil.py +2 -3
  25. braintrust/graph_util.py +10 -10
  26. braintrust/id_gen.py +2 -2
  27. braintrust/logger.py +346 -357
  28. braintrust/merge_row_batch.py +10 -9
  29. braintrust/oai.py +107 -24
  30. braintrust/otel/__init__.py +49 -49
  31. braintrust/otel/context.py +16 -30
  32. braintrust/otel/test_distributed_tracing.py +14 -11
  33. braintrust/otel/test_otel_bt_integration.py +32 -31
  34. braintrust/parameters.py +8 -8
  35. braintrust/prompt.py +14 -14
  36. braintrust/prompt_cache/disk_cache.py +5 -4
  37. braintrust/prompt_cache/lru_cache.py +3 -2
  38. braintrust/prompt_cache/prompt_cache.py +13 -14
  39. braintrust/queue.py +4 -4
  40. braintrust/score.py +4 -4
  41. braintrust/serializable_data_class.py +4 -4
  42. braintrust/span_identifier_v1.py +1 -2
  43. braintrust/span_identifier_v2.py +3 -4
  44. braintrust/span_identifier_v3.py +23 -20
  45. braintrust/span_identifier_v4.py +34 -25
  46. braintrust/test_framework.py +16 -6
  47. braintrust/test_helpers.py +5 -5
  48. braintrust/test_id_gen.py +2 -3
  49. braintrust/test_otel.py +61 -53
  50. braintrust/test_queue.py +0 -1
  51. braintrust/test_score.py +1 -3
  52. braintrust/test_span_components.py +29 -44
  53. braintrust/util.py +9 -8
  54. braintrust/version.py +2 -2
  55. braintrust/wrappers/_anthropic_utils.py +4 -4
  56. braintrust/wrappers/agno/__init__.py +3 -4
  57. braintrust/wrappers/agno/agent.py +1 -2
  58. braintrust/wrappers/agno/function_call.py +1 -2
  59. braintrust/wrappers/agno/model.py +1 -2
  60. braintrust/wrappers/agno/team.py +1 -2
  61. braintrust/wrappers/agno/utils.py +12 -12
  62. braintrust/wrappers/anthropic.py +7 -8
  63. braintrust/wrappers/claude_agent_sdk/__init__.py +3 -4
  64. braintrust/wrappers/claude_agent_sdk/_wrapper.py +29 -27
  65. braintrust/wrappers/dspy.py +15 -17
  66. braintrust/wrappers/google_genai/__init__.py +16 -16
  67. braintrust/wrappers/langchain.py +22 -24
  68. braintrust/wrappers/litellm.py +4 -3
  69. braintrust/wrappers/openai.py +15 -15
  70. braintrust/wrappers/pydantic_ai.py +1204 -0
  71. braintrust/wrappers/test_agno.py +0 -1
  72. braintrust/wrappers/test_dspy.py +0 -1
  73. braintrust/wrappers/test_google_genai.py +2 -3
  74. braintrust/wrappers/test_litellm.py +0 -1
  75. braintrust/wrappers/test_oai_attachments.py +322 -0
  76. braintrust/wrappers/test_pydantic_ai_integration.py +1788 -0
  77. braintrust/wrappers/{test_pydantic_ai.py → test_pydantic_ai_wrap_openai.py} +1 -2
  78. {braintrust-0.3.14.dist-info → braintrust-0.4.0.dist-info}/METADATA +3 -2
  79. braintrust-0.4.0.dist-info/RECORD +120 -0
  80. braintrust-0.3.14.dist-info/RECORD +0 -117
  81. {braintrust-0.3.14.dist-info → braintrust-0.4.0.dist-info}/WHEEL +0 -0
  82. {braintrust-0.3.14.dist-info → braintrust-0.4.0.dist-info}/entry_points.txt +0 -0
  83. {braintrust-0.3.14.dist-info → braintrust-0.4.0.dist-info}/top_level.txt +0 -0
braintrust/framework2.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import dataclasses
2
2
  import json
3
- from typing import Any, Callable, Dict, List, Optional, Union, overload
3
+ from collections.abc import Callable
4
+ from typing import Any, overload
4
5
 
5
6
  import slugify
6
7
  from braintrust.logger import api_conn, app_conn, login
@@ -20,7 +21,7 @@ from .util import eprint
20
21
 
21
22
  class ProjectIdCache:
22
23
  def __init__(self):
23
- self._cache: Dict[Project, str] = {}
24
+ self._cache: dict[Project, str] = {}
24
25
 
25
26
  def get(self, project: "Project") -> str:
26
27
  if project not in self._cache:
@@ -31,8 +32,8 @@ class ProjectIdCache:
31
32
 
32
33
  class _GlobalState:
33
34
  def __init__(self):
34
- self.functions: List[CodeFunction] = []
35
- self.prompts: List[CodePrompt] = []
35
+ self.functions: list[CodeFunction] = []
36
+ self.prompts: list[CodePrompt] = []
36
37
 
37
38
 
38
39
  global_ = _GlobalState()
@@ -47,11 +48,11 @@ class CodeFunction:
47
48
  name: str
48
49
  slug: str
49
50
  type_: str
50
- description: Optional[str]
51
+ description: str | None
51
52
  parameters: Any
52
53
  returns: Any
53
- if_exists: Optional[IfExists]
54
- metadata: Optional[Dict[str, Any]] = None
54
+ if_exists: IfExists | None
55
+ metadata: dict[str, Any] | None = None
55
56
 
56
57
 
57
58
  @dataclasses.dataclass
@@ -62,17 +63,17 @@ class CodePrompt:
62
63
  name: str
63
64
  slug: str
64
65
  prompt: PromptData
65
- tool_functions: List[Union[CodeFunction, SavedFunctionId]]
66
- description: Optional[str]
67
- function_type: Optional[str]
68
- id: Optional[str]
69
- if_exists: Optional[IfExists]
70
- metadata: Optional[Dict[str, Any]] = None
71
-
72
- def to_function_definition(self, if_exists: Optional[IfExists], project_ids: ProjectIdCache) -> Dict[str, Any]:
66
+ tool_functions: list[CodeFunction | SavedFunctionId]
67
+ description: str | None
68
+ function_type: str | None
69
+ id: str | None
70
+ if_exists: IfExists | None
71
+ metadata: dict[str, Any] | None = None
72
+
73
+ def to_function_definition(self, if_exists: IfExists | None, project_ids: ProjectIdCache) -> dict[str, Any]:
73
74
  prompt_data = self.prompt
74
75
  if len(self.tool_functions) > 0:
75
- resolvable_tool_functions: List[Any] = []
76
+ resolvable_tool_functions: list[Any] = []
76
77
  for f in self.tool_functions:
77
78
  if isinstance(f, CodeFunction):
78
79
  resolvable_tool_functions.append(
@@ -85,7 +86,7 @@ class CodePrompt:
85
86
  else:
86
87
  resolvable_tool_functions.append(f)
87
88
  prompt_data["tool_functions"] = resolvable_tool_functions
88
- j: Dict[str, Any] = {
89
+ j: dict[str, Any] = {
89
90
  "project_id": project_ids.get(self.project),
90
91
  "name": self.name,
91
92
  "slug": self.slug,
@@ -116,13 +117,13 @@ class ToolBuilder:
116
117
  self,
117
118
  *,
118
119
  handler: Callable[..., Any],
119
- name: Optional[str] = None,
120
- slug: Optional[str] = None,
121
- description: Optional[str] = None,
120
+ name: str | None = None,
121
+ slug: str | None = None,
122
+ description: str | None = None,
122
123
  parameters: Any = None,
123
124
  returns: Any = None,
124
- if_exists: Optional[IfExists] = None,
125
- metadata: Optional[Dict[str, Any]] = None,
125
+ if_exists: IfExists | None = None,
126
+ metadata: dict[str, Any] | None = None,
126
127
  ) -> CodeFunction:
127
128
  """Creates a tool.
128
129
 
@@ -175,48 +176,48 @@ class PromptBuilder:
175
176
  def create(
176
177
  self,
177
178
  *,
178
- name: Optional[str] = None,
179
- slug: Optional[str] = None,
180
- description: Optional[str] = None,
181
- id: Optional[str] = None,
179
+ name: str | None = None,
180
+ slug: str | None = None,
181
+ description: str | None = None,
182
+ id: str | None = None,
182
183
  prompt: str,
183
184
  model: str,
184
- params: Optional[ModelParams] = None,
185
- tools: Optional[List[Union[CodeFunction, SavedFunctionId, ToolFunctionDefinition]]] = None,
186
- if_exists: Optional[IfExists] = None,
187
- metadata: Optional[Dict[str, Any]] = None,
185
+ params: ModelParams | None = None,
186
+ tools: list[CodeFunction | SavedFunctionId | ToolFunctionDefinition] | None = None,
187
+ if_exists: IfExists | None = None,
188
+ metadata: dict[str, Any] | None = None,
188
189
  ) -> CodePrompt: ...
189
190
 
190
191
  @overload # messages only, no prompt
191
192
  def create(
192
193
  self,
193
194
  *,
194
- name: Optional[str] = None,
195
- slug: Optional[str] = None,
196
- description: Optional[str] = None,
197
- id: Optional[str] = None,
198
- messages: List[ChatCompletionMessageParam],
195
+ name: str | None = None,
196
+ slug: str | None = None,
197
+ description: str | None = None,
198
+ id: str | None = None,
199
+ messages: list[ChatCompletionMessageParam],
199
200
  model: str,
200
- params: Optional[ModelParams] = None,
201
- tools: Optional[List[Union[CodeFunction, SavedFunctionId, ToolFunctionDefinition]]] = None,
202
- if_exists: Optional[IfExists] = None,
203
- metadata: Optional[Dict[str, Any]] = None,
201
+ params: ModelParams | None = None,
202
+ tools: list[CodeFunction | SavedFunctionId | ToolFunctionDefinition] | None = None,
203
+ if_exists: IfExists | None = None,
204
+ metadata: dict[str, Any] | None = None,
204
205
  ) -> CodePrompt: ...
205
206
 
206
207
  def create(
207
208
  self,
208
209
  *,
209
- name: Optional[str] = None,
210
- slug: Optional[str] = None,
211
- description: Optional[str] = None,
212
- id: Optional[str] = None,
213
- prompt: Optional[str] = None,
214
- messages: Optional[List[ChatCompletionMessageParam]] = None,
210
+ name: str | None = None,
211
+ slug: str | None = None,
212
+ description: str | None = None,
213
+ id: str | None = None,
214
+ prompt: str | None = None,
215
+ messages: list[ChatCompletionMessageParam] | None = None,
215
216
  model: str,
216
- params: Optional[ModelParams] = None,
217
- tools: Optional[List[Union[CodeFunction, SavedFunctionId, ToolFunctionDefinition]]] = None,
218
- if_exists: Optional[IfExists] = None,
219
- metadata: Optional[Dict[str, Any]] = None,
217
+ params: ModelParams | None = None,
218
+ tools: list[CodeFunction | SavedFunctionId | ToolFunctionDefinition] | None = None,
219
+ if_exists: IfExists | None = None,
220
+ metadata: dict[str, Any] | None = None,
220
221
  ):
221
222
  """Creates a prompt.
222
223
 
@@ -239,8 +240,8 @@ class PromptBuilder:
239
240
  if not slug:
240
241
  slug = slugify.slugify(name)
241
242
 
242
- tool_functions: List[Union[CodeFunction, SavedFunctionId]] = []
243
- raw_tools: List[ToolFunctionDefinition] = []
243
+ tool_functions: list[CodeFunction | SavedFunctionId] = []
244
+ raw_tools: list[ToolFunctionDefinition] = []
244
245
  for tool in tools or []:
245
246
  if isinstance(tool, CodeFunction):
246
247
  tool_functions.append(tool)
@@ -298,11 +299,11 @@ class ScorerBuilder:
298
299
  def create(
299
300
  self,
300
301
  *,
301
- name: Optional[str] = None,
302
- slug: Optional[str] = None,
303
- description: Optional[str] = None,
304
- if_exists: Optional[IfExists] = None,
305
- metadata: Optional[Dict[str, Any]] = None,
302
+ name: str | None = None,
303
+ slug: str | None = None,
304
+ description: str | None = None,
305
+ if_exists: IfExists | None = None,
306
+ metadata: dict[str, Any] | None = None,
306
307
  handler: Callable[..., Any],
307
308
  parameters: Any,
308
309
  returns: Any = None,
@@ -313,16 +314,16 @@ class ScorerBuilder:
313
314
  def create(
314
315
  self,
315
316
  *,
316
- name: Optional[str] = None,
317
- slug: Optional[str] = None,
318
- description: Optional[str] = None,
319
- if_exists: Optional[IfExists] = None,
320
- metadata: Optional[Dict[str, Any]] = None,
317
+ name: str | None = None,
318
+ slug: str | None = None,
319
+ description: str | None = None,
320
+ if_exists: IfExists | None = None,
321
+ metadata: dict[str, Any] | None = None,
321
322
  prompt: str,
322
323
  model: str,
323
- params: Optional[ModelParams] = None,
324
+ params: ModelParams | None = None,
324
325
  use_cot: bool,
325
- choice_scores: Dict[str, float],
326
+ choice_scores: dict[str, float],
326
327
  ) -> CodePrompt: ...
327
328
 
328
329
  # LLM scorer with messages.
@@ -330,38 +331,38 @@ class ScorerBuilder:
330
331
  def create(
331
332
  self,
332
333
  *,
333
- name: Optional[str] = None,
334
- slug: Optional[str] = None,
335
- description: Optional[str] = None,
336
- if_exists: Optional[IfExists] = None,
337
- metadata: Optional[Dict[str, Any]] = None,
338
- messages: List[ChatCompletionMessageParam],
334
+ name: str | None = None,
335
+ slug: str | None = None,
336
+ description: str | None = None,
337
+ if_exists: IfExists | None = None,
338
+ metadata: dict[str, Any] | None = None,
339
+ messages: list[ChatCompletionMessageParam],
339
340
  model: str,
340
- params: Optional[ModelParams] = None,
341
+ params: ModelParams | None = None,
341
342
  use_cot: bool,
342
- choice_scores: Dict[str, float],
343
+ choice_scores: dict[str, float],
343
344
  ) -> CodePrompt: ...
344
345
 
345
346
  def create(
346
347
  self,
347
348
  *,
348
- name: Optional[str] = None,
349
- slug: Optional[str] = None,
350
- description: Optional[str] = None,
351
- if_exists: Optional[IfExists] = None,
352
- metadata: Optional[Dict[str, Any]] = None,
349
+ name: str | None = None,
350
+ slug: str | None = None,
351
+ description: str | None = None,
352
+ if_exists: IfExists | None = None,
353
+ metadata: dict[str, Any] | None = None,
353
354
  # Code scorer params.
354
- handler: Optional[Callable[..., Any]] = None,
355
+ handler: Callable[..., Any] | None = None,
355
356
  parameters: Any = None,
356
357
  returns: Any = None,
357
358
  # LLM scorer params.
358
- prompt: Optional[str] = None,
359
- messages: Optional[List[ChatCompletionMessageParam]] = None,
360
- model: Optional[str] = None,
361
- params: Optional[ModelParams] = None,
362
- use_cot: Optional[bool] = None,
363
- choice_scores: Optional[Dict[str, float]] = None,
364
- ) -> Union[CodeFunction, CodePrompt]:
359
+ prompt: str | None = None,
360
+ messages: list[ChatCompletionMessageParam] | None = None,
361
+ model: str | None = None,
362
+ params: ModelParams | None = None,
363
+ use_cot: bool | None = None,
364
+ choice_scores: dict[str, float] | None = None,
365
+ ) -> CodeFunction | CodePrompt:
365
366
  """Creates a scorer.
366
367
 
367
368
  Args:
@@ -462,8 +463,8 @@ class Project:
462
463
  self.prompts = PromptBuilder(self)
463
464
  self.scorers = ScorerBuilder(self)
464
465
 
465
- self._publishable_code_functions: List[CodeFunction] = []
466
- self._publishable_prompts: List[CodePrompt] = []
466
+ self._publishable_code_functions: list[CodeFunction] = []
467
+ self._publishable_prompts: list[CodePrompt] = []
467
468
 
468
469
  def add_code_function(self, fn: CodeFunction):
469
470
  self._publishable_code_functions.append(fn)
@@ -483,7 +484,7 @@ class Project:
483
484
  login()
484
485
  project_id_cache = ProjectIdCache()
485
486
 
486
- definitions: List[Dict[str, Any]] = []
487
+ definitions: list[dict[str, Any]] = []
487
488
  if self._publishable_code_functions:
488
489
  eprint(
489
490
  f"{bcolors.WARNING}Code functions cannot be published directly. Use `braintrust push` instead.{bcolors.ENDC}"
@@ -1,38 +1,58 @@
1
- from typing import Any, Dict, List, Literal, Optional, TypeVar, Union, overload
1
+ from typing import Any, Literal, TypedDict, TypeVar, overload
2
2
 
3
3
  from sseclient import SSEClient
4
4
 
5
+ from .._generated_types import FunctionTypeEnum, InvokeContext
5
6
  from ..logger import Exportable, get_span_parent_object, login, proxy_conn
6
7
  from ..util import response_raise_for_status
7
8
  from .constants import INVOKE_API_VERSION
8
9
  from .stream import BraintrustInvokeError, BraintrustStream
9
10
 
10
11
  T = TypeVar("T")
11
- ModeType = Literal["auto", "parallel"]
12
+ ModeType = Literal["auto", "parallel", "json", "text"]
13
+ ObjectType = Literal["project_logs", "experiment", "dataset", "playground_logs"]
14
+
15
+
16
+ class SpanScope(TypedDict):
17
+ """Scope for operating on a single span."""
18
+
19
+ type: Literal["span"]
20
+ id: str
21
+ root_span_id: str
22
+
23
+
24
+ class TraceScope(TypedDict):
25
+ """Scope for operating on an entire trace."""
26
+
27
+ type: Literal["trace"]
28
+ root_span_id: str
12
29
 
13
30
 
14
31
  @overload
15
32
  def invoke(
16
33
  # the permutations of arguments for a function id
17
- function_id: Optional[str] = None,
18
- version: Optional[str] = None,
19
- prompt_session_id: Optional[str] = None,
20
- prompt_session_function_id: Optional[str] = None,
21
- project_name: Optional[str] = None,
22
- slug: Optional[str] = None,
23
- global_function: Optional[str] = None,
34
+ function_id: str | None = None,
35
+ version: str | None = None,
36
+ prompt_session_id: str | None = None,
37
+ prompt_session_function_id: str | None = None,
38
+ project_name: str | None = None,
39
+ project_id: str | None = None,
40
+ slug: str | None = None,
41
+ global_function: str | None = None,
42
+ function_type: FunctionTypeEnum | None = None,
24
43
  # arguments to the function
25
44
  input: Any = None,
26
- messages: Optional[List[Any]] = None,
27
- metadata: Optional[Dict[str, Any]] = None,
28
- tags: Optional[List[str]] = None,
29
- parent: Optional[Union[Exportable, str]] = None,
30
- stream: Optional[Literal[False]] = None,
31
- mode: Optional[ModeType] = None,
32
- strict: Optional[bool] = None,
33
- org_name: Optional[str] = None,
34
- api_key: Optional[str] = None,
35
- app_url: Optional[str] = None,
45
+ messages: list[Any] | None = None,
46
+ context: InvokeContext | None = None,
47
+ metadata: dict[str, Any] | None = None,
48
+ tags: list[str] | None = None,
49
+ parent: Exportable | str | None = None,
50
+ stream: Literal[False] | None = None,
51
+ mode: ModeType | None = None,
52
+ strict: bool | None = None,
53
+ org_name: str | None = None,
54
+ api_key: str | None = None,
55
+ app_url: str | None = None,
36
56
  force_login: bool = False,
37
57
  ) -> T: ...
38
58
 
@@ -40,52 +60,58 @@ def invoke(
40
60
  @overload
41
61
  def invoke(
42
62
  # the permutations of arguments for a function id
43
- function_id: Optional[str] = None,
44
- version: Optional[str] = None,
45
- prompt_session_id: Optional[str] = None,
46
- prompt_session_function_id: Optional[str] = None,
47
- project_name: Optional[str] = None,
48
- slug: Optional[str] = None,
49
- global_function: Optional[str] = None,
63
+ function_id: str | None = None,
64
+ version: str | None = None,
65
+ prompt_session_id: str | None = None,
66
+ prompt_session_function_id: str | None = None,
67
+ project_name: str | None = None,
68
+ project_id: str | None = None,
69
+ slug: str | None = None,
70
+ global_function: str | None = None,
71
+ function_type: FunctionTypeEnum | None = None,
50
72
  # arguments to the function
51
73
  input: Any = None,
52
- messages: Optional[List[Any]] = None,
53
- metadata: Optional[Dict[str, Any]] = None,
54
- tags: Optional[List[str]] = None,
55
- parent: Optional[Union[Exportable, str]] = None,
74
+ messages: list[Any] | None = None,
75
+ context: InvokeContext | None = None,
76
+ metadata: dict[str, Any] | None = None,
77
+ tags: list[str] | None = None,
78
+ parent: Exportable | str | None = None,
56
79
  stream: Literal[True] = True,
57
- mode: Optional[ModeType] = None,
58
- strict: Optional[bool] = None,
59
- org_name: Optional[str] = None,
60
- api_key: Optional[str] = None,
61
- app_url: Optional[str] = None,
80
+ mode: ModeType | None = None,
81
+ strict: bool | None = None,
82
+ org_name: str | None = None,
83
+ api_key: str | None = None,
84
+ app_url: str | None = None,
62
85
  force_login: bool = False,
63
86
  ) -> BraintrustStream: ...
64
87
 
65
88
 
66
89
  def invoke(
67
90
  # the permutations of arguments for a function id
68
- function_id: Optional[str] = None,
69
- version: Optional[str] = None,
70
- prompt_session_id: Optional[str] = None,
71
- prompt_session_function_id: Optional[str] = None,
72
- project_name: Optional[str] = None,
73
- slug: Optional[str] = None,
74
- global_function: Optional[str] = None,
91
+ function_id: str | None = None,
92
+ version: str | None = None,
93
+ prompt_session_id: str | None = None,
94
+ prompt_session_function_id: str | None = None,
95
+ project_name: str | None = None,
96
+ project_id: str | None = None,
97
+ slug: str | None = None,
98
+ global_function: str | None = None,
99
+ function_type: FunctionTypeEnum | None = None,
75
100
  # arguments to the function
76
101
  input: Any = None,
77
- messages: Optional[List[Any]] = None,
78
- metadata: Optional[Dict[str, Any]] = None,
79
- tags: Optional[List[str]] = None,
80
- parent: Optional[Union[Exportable, str]] = None,
102
+ messages: list[Any] | None = None,
103
+ context: InvokeContext | None = None,
104
+ metadata: dict[str, Any] | None = None,
105
+ tags: list[str] | None = None,
106
+ parent: Exportable | str | None = None,
81
107
  stream: bool = False,
82
- mode: Optional[ModeType] = None,
83
- strict: Optional[bool] = None,
84
- org_name: Optional[str] = None,
85
- api_key: Optional[str] = None,
86
- app_url: Optional[str] = None,
108
+ mode: ModeType | None = None,
109
+ strict: bool | None = None,
110
+ org_name: str | None = None,
111
+ api_key: str | None = None,
112
+ app_url: str | None = None,
87
113
  force_login: bool = False,
88
- ) -> Union[BraintrustStream, T]:
114
+ ) -> BraintrustStream | T:
89
115
  """
90
116
  Invoke a Braintrust function, returning a `BraintrustStream` or the value as a plain
91
117
  Python object.
@@ -93,6 +119,8 @@ def invoke(
93
119
  Args:
94
120
  input: The input to the function. This will be logged as the `input` field in the span.
95
121
  messages: Additional OpenAI-style messages to add to the prompt (only works for llm functions).
122
+ context: Context for functions that operate on spans/traces (e.g., facets). Should contain
123
+ `object_type`, `object_id`, and `scope` fields.
96
124
  metadata: Additional metadata to add to the span. This will be logged as the `metadata` field in the span.
97
125
  It will also be available as the {{metadata}} field in the prompt and as the `metadata` argument
98
126
  to the function.
@@ -118,8 +146,12 @@ def invoke(
118
146
  prompt_session_id: The ID of the prompt session to invoke the function from.
119
147
  prompt_session_function_id: The ID of the function in the prompt session to invoke.
120
148
  project_name: The name of the project containing the function to invoke.
149
+ project_id: The ID of the project to use for execution context (API keys, project defaults, etc.).
150
+ This is not the project the function belongs to, but the project context for the invocation.
121
151
  slug: The slug of the function to invoke.
122
152
  global_function: The name of the global function to invoke.
153
+ function_type: The type of the global function to invoke. If unspecified, defaults to 'scorer'
154
+ for backward compatibility.
123
155
 
124
156
  Returns:
125
157
  The output of the function. If `stream` is True, returns a `BraintrustStream`,
@@ -149,6 +181,8 @@ def invoke(
149
181
  function_id_args["slug"] = slug
150
182
  if global_function is not None:
151
183
  function_id_args["global_function"] = global_function
184
+ if function_type is not None:
185
+ function_id_args["function_type"] = function_type
152
186
 
153
187
  request = dict(
154
188
  input=input,
@@ -161,12 +195,18 @@ def invoke(
161
195
  )
162
196
  if messages is not None:
163
197
  request["messages"] = messages
198
+ if context is not None:
199
+ request["context"] = context
164
200
  if mode is not None:
165
201
  request["mode"] = mode
166
202
  if strict is not None:
167
203
  request["strict"] = strict
168
204
 
169
205
  headers = {"Accept": "text/event-stream" if stream else "application/json"}
206
+ if project_id is not None:
207
+ headers["x-bt-project-id"] = project_id
208
+ if org_name is not None:
209
+ headers["x-bt-org-name"] = org_name
170
210
 
171
211
  resp = proxy_conn().post("function/invoke", json=request, headers=headers, stream=stream)
172
212
  if resp.status_code == 500:
@@ -180,7 +220,7 @@ def invoke(
180
220
  return resp.json()
181
221
 
182
222
 
183
- def init_function(project_name: str, slug: str, version: Optional[str] = None):
223
+ def init_function(project_name: str, slug: str, version: str | None = None):
184
224
  """
185
225
  Creates a function that can be used as either a task or scorer in the Eval framework.
186
226
  When used as a task, it will invoke the specified Braintrust function with the input.
@@ -7,8 +7,9 @@ with utility methods to make them easy to log and convert into various formats.
7
7
 
8
8
  import dataclasses
9
9
  import json
10
+ from collections.abc import Generator
10
11
  from itertools import tee
11
- from typing import Generator, List, Literal, Union
12
+ from typing import Literal, Union
12
13
 
13
14
  from sseclient import SSEClient
14
15
 
@@ -87,7 +88,7 @@ class BraintrustStream:
87
88
  with utility methods to make them easy to log and convert into various formats.
88
89
  """
89
90
 
90
- def __init__(self, base_stream: Union[SSEClient, List[BraintrustStreamChunk]]):
91
+ def __init__(self, base_stream: SSEClient | list[BraintrustStreamChunk]):
91
92
  """
92
93
  Initialize a BraintrustStream.
93
94
 
@@ -1,4 +1,4 @@
1
- """Auto-generated file (internal git SHA 8e9c0a96b3cf291360978c17580f72f6817bd6c8) -- do not modify"""
1
+ """Auto-generated file (internal git SHA 547fa17c0937e0e25fdf9214487be6f31c91a37a) -- do not modify"""
2
2
 
3
3
  from ._generated_types import (
4
4
  Acl,
@@ -32,6 +32,7 @@ from ._generated_types import (
32
32
  ExperimentEvent,
33
33
  ExtendedSavedFunctionId,
34
34
  ExternalAttachmentReference,
35
+ FacetData,
35
36
  Function,
36
37
  FunctionData,
37
38
  FunctionFormat,
@@ -47,10 +48,15 @@ from ._generated_types import (
47
48
  GraphNode,
48
49
  Group,
49
50
  IfExists,
51
+ InvokeContext,
50
52
  InvokeFunction,
51
53
  InvokeParent,
54
+ InvokeScope,
55
+ MCPServer,
52
56
  MessageRole,
53
57
  ModelParams,
58
+ NullableFunctionTypeEnum,
59
+ NullableSavedFunctionId,
54
60
  ObjectReference,
55
61
  ObjectReferenceNullish,
56
62
  OnlineScoreConfig,
@@ -86,11 +92,13 @@ from ._generated_types import (
86
92
  ServiceToken,
87
93
  SpanAttributes,
88
94
  SpanIFrame,
95
+ SpanScope,
89
96
  SpanType,
90
97
  SSEConsoleEventData,
91
98
  SSEProgressEventData,
92
99
  StreamingMode,
93
100
  ToolFunctionDefinition,
101
+ TraceScope,
94
102
  UploadStatus,
95
103
  User,
96
104
  View,
@@ -131,6 +139,7 @@ __all__ = [
131
139
  "ExperimentEvent",
132
140
  "ExtendedSavedFunctionId",
133
141
  "ExternalAttachmentReference",
142
+ "FacetData",
134
143
  "Function",
135
144
  "FunctionData",
136
145
  "FunctionFormat",
@@ -146,10 +155,15 @@ __all__ = [
146
155
  "GraphNode",
147
156
  "Group",
148
157
  "IfExists",
158
+ "InvokeContext",
149
159
  "InvokeFunction",
150
160
  "InvokeParent",
161
+ "InvokeScope",
162
+ "MCPServer",
151
163
  "MessageRole",
152
164
  "ModelParams",
165
+ "NullableFunctionTypeEnum",
166
+ "NullableSavedFunctionId",
153
167
  "ObjectReference",
154
168
  "ObjectReferenceNullish",
155
169
  "OnlineScoreConfig",
@@ -187,9 +201,11 @@ __all__ = [
187
201
  "ServiceToken",
188
202
  "SpanAttributes",
189
203
  "SpanIFrame",
204
+ "SpanScope",
190
205
  "SpanType",
191
206
  "StreamingMode",
192
207
  "ToolFunctionDefinition",
208
+ "TraceScope",
193
209
  "UploadStatus",
194
210
  "User",
195
211
  "View",
braintrust/git_fields.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import List, Literal, Optional
2
+ from typing import Literal
3
3
 
4
4
  from .serializable_data_class import SerializableDataClass
5
5
 
@@ -8,21 +8,21 @@ from .serializable_data_class import SerializableDataClass
8
8
  class RepoInfo(SerializableDataClass):
9
9
  """Information about the current HEAD of the repo."""
10
10
 
11
- commit: Optional[str] = None
12
- branch: Optional[str] = None
13
- tag: Optional[str] = None
14
- dirty: Optional[bool] = None
15
- author_name: Optional[str] = None
16
- author_email: Optional[str] = None
17
- commit_message: Optional[str] = None
18
- commit_time: Optional[str] = None
19
- git_diff: Optional[str] = None
11
+ commit: str | None = None
12
+ branch: str | None = None
13
+ tag: str | None = None
14
+ dirty: bool | None = None
15
+ author_name: str | None = None
16
+ author_email: str | None = None
17
+ commit_message: str | None = None
18
+ commit_time: str | None = None
19
+ git_diff: str | None = None
20
20
 
21
21
 
22
22
  @dataclass
23
23
  class GitMetadataSettings(SerializableDataClass):
24
24
  collect: Literal["all", "some", "none"] = "all"
25
- fields: Optional[List[str]] = field(default_factory=list)
25
+ fields: list[str] | None = field(default_factory=list)
26
26
 
27
27
  @classmethod
28
28
  def merge(cls, s1: "GitMetadataSettings", s2: "GitMetadataSettings") -> "GitMetadataSettings":