mirascope 2.0.1__py3-none-any.whl → 2.1.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.
- mirascope/_stubs.py +39 -18
- mirascope/_utils.py +34 -0
- mirascope/api/_generated/__init__.py +4 -0
- mirascope/api/_generated/organization_invitations/client.py +2 -2
- mirascope/api/_generated/organization_invitations/raw_client.py +2 -2
- mirascope/api/_generated/project_memberships/__init__.py +4 -0
- mirascope/api/_generated/project_memberships/client.py +91 -0
- mirascope/api/_generated/project_memberships/raw_client.py +239 -0
- mirascope/api/_generated/project_memberships/types/__init__.py +4 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_get_response.py +33 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_get_response_role.py +7 -0
- mirascope/api/_generated/reference.md +73 -1
- mirascope/llm/__init__.py +19 -0
- mirascope/llm/calls/calls.py +28 -21
- mirascope/llm/calls/decorator.py +17 -24
- mirascope/llm/formatting/__init__.py +2 -2
- mirascope/llm/formatting/format.py +2 -4
- mirascope/llm/formatting/types.py +19 -2
- mirascope/llm/models/models.py +66 -146
- mirascope/llm/prompts/decorator.py +5 -16
- mirascope/llm/prompts/prompts.py +35 -38
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +22 -7
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +22 -16
- mirascope/llm/providers/anthropic/_utils/decode.py +45 -7
- mirascope/llm/providers/anthropic/_utils/encode.py +28 -15
- mirascope/llm/providers/anthropic/beta_provider.py +33 -69
- mirascope/llm/providers/anthropic/provider.py +52 -91
- mirascope/llm/providers/base/_utils.py +4 -9
- mirascope/llm/providers/base/base_provider.py +89 -205
- mirascope/llm/providers/google/_utils/decode.py +51 -1
- mirascope/llm/providers/google/_utils/encode.py +38 -21
- mirascope/llm/providers/google/provider.py +33 -69
- mirascope/llm/providers/mirascope/provider.py +25 -61
- mirascope/llm/providers/mlx/encoding/base.py +3 -6
- mirascope/llm/providers/mlx/encoding/transformers.py +4 -8
- mirascope/llm/providers/mlx/mlx.py +9 -21
- mirascope/llm/providers/mlx/provider.py +33 -69
- mirascope/llm/providers/openai/completions/_utils/encode.py +39 -20
- mirascope/llm/providers/openai/completions/base_provider.py +34 -75
- mirascope/llm/providers/openai/provider.py +25 -61
- mirascope/llm/providers/openai/responses/_utils/decode.py +31 -2
- mirascope/llm/providers/openai/responses/_utils/encode.py +32 -17
- mirascope/llm/providers/openai/responses/provider.py +34 -75
- mirascope/llm/responses/__init__.py +2 -1
- mirascope/llm/responses/base_stream_response.py +4 -0
- mirascope/llm/responses/response.py +8 -12
- mirascope/llm/responses/stream_response.py +8 -12
- mirascope/llm/responses/usage.py +44 -0
- mirascope/llm/tools/__init__.py +24 -0
- mirascope/llm/tools/provider_tools.py +18 -0
- mirascope/llm/tools/tool_schema.py +11 -4
- mirascope/llm/tools/toolkit.py +24 -6
- mirascope/llm/tools/types.py +112 -0
- mirascope/llm/tools/web_search_tool.py +32 -0
- mirascope/ops/__init__.py +19 -1
- mirascope/ops/_internal/closure.py +4 -1
- mirascope/ops/_internal/exporters/exporters.py +13 -46
- mirascope/ops/_internal/exporters/utils.py +37 -0
- mirascope/ops/_internal/instrumentation/__init__.py +20 -0
- mirascope/ops/_internal/instrumentation/llm/common.py +19 -49
- mirascope/ops/_internal/instrumentation/llm/model.py +61 -82
- mirascope/ops/_internal/instrumentation/llm/serialize.py +36 -12
- mirascope/ops/_internal/instrumentation/providers/__init__.py +29 -0
- mirascope/ops/_internal/instrumentation/providers/anthropic.py +78 -0
- mirascope/ops/_internal/instrumentation/providers/base.py +179 -0
- mirascope/ops/_internal/instrumentation/providers/google_genai.py +85 -0
- mirascope/ops/_internal/instrumentation/providers/openai.py +82 -0
- mirascope/ops/_internal/traced_calls.py +14 -0
- mirascope/ops/_internal/traced_functions.py +7 -2
- mirascope/ops/_internal/utils.py +12 -4
- mirascope/ops/_internal/versioned_functions.py +1 -1
- {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/METADATA +96 -68
- {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/RECORD +75 -64
- {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/WHEEL +0 -0
- {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/licenses/LICENSE +0 -0
mirascope/llm/responses/usage.py
CHANGED
|
@@ -6,6 +6,38 @@ from dataclasses import dataclass
|
|
|
6
6
|
from typing import Any, Literal
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
@dataclass(kw_only=True)
|
|
10
|
+
class ProviderToolUsage:
|
|
11
|
+
"""Usage data for a provider's server-side tool.
|
|
12
|
+
|
|
13
|
+
Tracks usage of tools executed by the provider that have separate pricing
|
|
14
|
+
beyond standard token costs (e.g., web search, code execution).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
name: str
|
|
18
|
+
"""Tool name matching our ProviderTool.name (e.g., "web_search").
|
|
19
|
+
|
|
20
|
+
This is the consistent cross-provider identifier that matches
|
|
21
|
+
the corresponding Mirascope ProviderTool class (e.g., WebSearchTool.name).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
call_count: int = 0
|
|
25
|
+
"""Number of invocations/calls of this tool."""
|
|
26
|
+
|
|
27
|
+
duration_seconds: float | None = None
|
|
28
|
+
"""Duration in seconds for time-based tools (e.g., code execution).
|
|
29
|
+
|
|
30
|
+
None if not applicable to this tool type.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
metadata: dict[str, Any] | None = None
|
|
34
|
+
"""Provider-specific metadata for debugging/analytics.
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
- Google grounding: {"queries": ["query1", "query2"]}
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
|
|
9
41
|
@dataclass(kw_only=True)
|
|
10
42
|
class UsageDeltaChunk:
|
|
11
43
|
"""A chunk containing incremental token usage information from a streaming response.
|
|
@@ -31,6 +63,9 @@ class UsageDeltaChunk:
|
|
|
31
63
|
reasoning_tokens: int = 0
|
|
32
64
|
"""Delta in reasoning/thinking tokens."""
|
|
33
65
|
|
|
66
|
+
provider_tool_usage: list[ProviderToolUsage] | None = None
|
|
67
|
+
"""Provider tool usage (emitted in final chunk only)."""
|
|
68
|
+
|
|
34
69
|
|
|
35
70
|
@dataclass(kw_only=True)
|
|
36
71
|
class Usage:
|
|
@@ -86,6 +121,15 @@ class Usage:
|
|
|
86
121
|
Will be 0 if not reported by the provider or if the model does not support reasoning.
|
|
87
122
|
"""
|
|
88
123
|
|
|
124
|
+
provider_tool_usage: list[ProviderToolUsage] | None = None
|
|
125
|
+
"""Provider tool usage data for tools with separate pricing.
|
|
126
|
+
|
|
127
|
+
Tracks usage of server-side tools executed by the provider (e.g., web search,
|
|
128
|
+
code execution) that have pricing beyond standard token costs.
|
|
129
|
+
|
|
130
|
+
Will be None if no provider tools were used.
|
|
131
|
+
"""
|
|
132
|
+
|
|
89
133
|
raw: Any = None
|
|
90
134
|
"""The raw usage object from the provider."""
|
|
91
135
|
|
mirascope/llm/tools/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .decorator import ToolDecorator, tool
|
|
4
4
|
from .protocols import AsyncContextToolFn, AsyncToolFn, ContextToolFn, ToolFn
|
|
5
|
+
from .provider_tools import ProviderTool
|
|
5
6
|
from .tool_schema import (
|
|
6
7
|
FORMAT_TOOL_NAME,
|
|
7
8
|
AnyToolFn,
|
|
@@ -19,21 +20,38 @@ from .toolkit import (
|
|
|
19
20
|
ToolkitT,
|
|
20
21
|
)
|
|
21
22
|
from .tools import AsyncContextTool, AsyncTool, ContextTool, Tool, ToolT
|
|
23
|
+
from .types import (
|
|
24
|
+
AnyTools,
|
|
25
|
+
AsyncContextTools,
|
|
26
|
+
AsyncTools,
|
|
27
|
+
ContextTools,
|
|
28
|
+
Tools,
|
|
29
|
+
normalize_async_context_tools,
|
|
30
|
+
normalize_async_tools,
|
|
31
|
+
normalize_context_tools,
|
|
32
|
+
normalize_tools,
|
|
33
|
+
)
|
|
34
|
+
from .web_search_tool import WebSearchTool
|
|
22
35
|
|
|
23
36
|
__all__ = [
|
|
24
37
|
"FORMAT_TOOL_NAME",
|
|
25
38
|
"AnyToolFn",
|
|
26
39
|
"AnyToolSchema",
|
|
40
|
+
"AnyTools",
|
|
27
41
|
"AsyncContextTool",
|
|
28
42
|
"AsyncContextToolFn",
|
|
29
43
|
"AsyncContextToolkit",
|
|
44
|
+
"AsyncContextTools",
|
|
30
45
|
"AsyncTool",
|
|
31
46
|
"AsyncToolFn",
|
|
32
47
|
"AsyncToolkit",
|
|
48
|
+
"AsyncTools",
|
|
33
49
|
"BaseToolkit",
|
|
34
50
|
"ContextTool",
|
|
35
51
|
"ContextToolFn",
|
|
36
52
|
"ContextToolkit",
|
|
53
|
+
"ContextTools",
|
|
54
|
+
"ProviderTool",
|
|
37
55
|
"Tool",
|
|
38
56
|
"ToolDecorator",
|
|
39
57
|
"ToolFn",
|
|
@@ -43,5 +61,11 @@ __all__ = [
|
|
|
43
61
|
"ToolT",
|
|
44
62
|
"Toolkit",
|
|
45
63
|
"ToolkitT",
|
|
64
|
+
"Tools",
|
|
65
|
+
"WebSearchTool",
|
|
66
|
+
"normalize_async_context_tools",
|
|
67
|
+
"normalize_async_tools",
|
|
68
|
+
"normalize_context_tools",
|
|
69
|
+
"normalize_tools",
|
|
46
70
|
"tool",
|
|
47
71
|
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Base class for provider-native tools."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class ProviderTool:
|
|
8
|
+
"""Base class for tools executed natively by providers.
|
|
9
|
+
|
|
10
|
+
Unlike regular tools which define functions that you execute locally,
|
|
11
|
+
provider tools are capabilities built into the provider's API.
|
|
12
|
+
The provider handles execution entirely server-side.
|
|
13
|
+
|
|
14
|
+
Provider tools have no sync/async distinction since they are not
|
|
15
|
+
executed by your code - they are configuration passed to the provider.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
name: str
|
|
@@ -22,6 +22,7 @@ from docstring_parser import parse
|
|
|
22
22
|
from pydantic import BaseModel, Field, create_model
|
|
23
23
|
from pydantic.fields import FieldInfo
|
|
24
24
|
|
|
25
|
+
from ..._utils import copy_function_metadata
|
|
25
26
|
from ..content import ToolCall
|
|
26
27
|
from ..types import Jsonable
|
|
27
28
|
from .protocols import AsyncContextToolFn, AsyncToolFn, ContextToolFn, ToolFn
|
|
@@ -39,8 +40,7 @@ ToolFnT = TypeVar(
|
|
|
39
40
|
covariant=True,
|
|
40
41
|
)
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
ToolSchemaT = TypeVar("ToolSchemaT", bound=AnyToolSchema, covariant=True)
|
|
43
|
+
ToolSchemaT = TypeVar("ToolSchemaT", bound="ToolSchema[AnyToolFn]", covariant=True)
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
ModelJsonSchema = TypedDict(
|
|
@@ -159,11 +159,14 @@ class ToolSchema(Generic[ToolFnT]):
|
|
|
159
159
|
|
|
160
160
|
strict: bool | None
|
|
161
161
|
"""Whether the tool should use strict mode when supported by the model.
|
|
162
|
-
|
|
163
|
-
If set to None, will use the provider's default setting (usually as strict as
|
|
162
|
+
|
|
163
|
+
If set to None, will use the provider's default setting (usually as strict as
|
|
164
164
|
possible).
|
|
165
165
|
"""
|
|
166
166
|
|
|
167
|
+
__name__: str
|
|
168
|
+
"""The name of the underlying function (preserved for decorator stacking)."""
|
|
169
|
+
|
|
167
170
|
def __hash__(self) -> int:
|
|
168
171
|
if not hasattr(self, "_hash"):
|
|
169
172
|
self._hash = hash(
|
|
@@ -200,6 +203,7 @@ class ToolSchema(Generic[ToolFnT]):
|
|
|
200
203
|
self.description = description
|
|
201
204
|
self.parameters = parameters
|
|
202
205
|
self.strict = strict
|
|
206
|
+
copy_function_metadata(self, fn)
|
|
203
207
|
|
|
204
208
|
@classmethod
|
|
205
209
|
def from_function(
|
|
@@ -312,3 +316,6 @@ class ToolSchema(Generic[ToolFnT]):
|
|
|
312
316
|
is performed.
|
|
313
317
|
"""
|
|
314
318
|
return tool_call.name == self.name
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
AnyToolSchema: TypeAlias = ToolSchema[AnyToolFn]
|
mirascope/llm/tools/toolkit.py
CHANGED
|
@@ -6,6 +6,7 @@ from ..content import ToolCall, ToolOutput
|
|
|
6
6
|
from ..context import Context, DepsT
|
|
7
7
|
from ..exceptions import ToolNotFoundError
|
|
8
8
|
from ..types import Jsonable
|
|
9
|
+
from .provider_tools import ProviderTool
|
|
9
10
|
from .tool_schema import ToolSchemaT
|
|
10
11
|
from .tools import AsyncContextTool, AsyncTool, ContextTool, Tool
|
|
11
12
|
|
|
@@ -24,13 +25,24 @@ class BaseToolkit(Generic[ToolSchemaT]):
|
|
|
24
25
|
including name validation and tool lookup.
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
|
-
tools: Sequence[ToolSchemaT]
|
|
28
|
+
tools: Sequence[ToolSchemaT | ProviderTool]
|
|
28
29
|
"""The tools included in the toolkit."""
|
|
29
30
|
|
|
30
31
|
tools_dict: dict[str, ToolSchemaT]
|
|
31
|
-
"""A mapping from tool names to tools in the toolkit.
|
|
32
|
+
"""A mapping from tool names to tools in the toolkit.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
This dict does not include any `ProviderTool`s, since they do not correspond
|
|
35
|
+
to tool calls that your code executes.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
provider_tools_dict: dict[str, ProviderTool]
|
|
39
|
+
"""A mapping from provider tool names to provider tools in the toolkit.
|
|
40
|
+
|
|
41
|
+
Provider tools are capabilities built into the provider's API (like web search)
|
|
42
|
+
that are executed server-side, not by your code.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, tools: Sequence[ToolSchemaT | ProviderTool] | None) -> None:
|
|
34
46
|
"""Initialize the toolkit with a collection of tools.
|
|
35
47
|
|
|
36
48
|
Args:
|
|
@@ -41,10 +53,16 @@ class BaseToolkit(Generic[ToolSchemaT]):
|
|
|
41
53
|
"""
|
|
42
54
|
self.tools = tools or []
|
|
43
55
|
self.tools_dict = {}
|
|
56
|
+
self.provider_tools_dict = {}
|
|
44
57
|
for tool in self.tools:
|
|
45
|
-
if tool
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
if isinstance(tool, ProviderTool):
|
|
59
|
+
if tool.name in self.provider_tools_dict:
|
|
60
|
+
raise ValueError(f"Multiple provider tools with name: {tool.name}")
|
|
61
|
+
self.provider_tools_dict[tool.name] = tool
|
|
62
|
+
else:
|
|
63
|
+
if tool.name in self.tools_dict:
|
|
64
|
+
raise ValueError(f"Multiple tools with name: {tool.name}")
|
|
65
|
+
self.tools_dict[tool.name] = tool
|
|
48
66
|
|
|
49
67
|
def get(self, tool_call: ToolCall) -> ToolSchemaT:
|
|
50
68
|
"""Get a tool that can execute a specific tool call.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Type aliases for tool parameter types used in provider signatures."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from typing import TypeAlias
|
|
7
|
+
from typing_extensions import TypeAliasType
|
|
8
|
+
|
|
9
|
+
from ..context import DepsT
|
|
10
|
+
from .provider_tools import ProviderTool
|
|
11
|
+
from .tool_schema import AnyToolSchema
|
|
12
|
+
from .toolkit import (
|
|
13
|
+
AsyncContextToolkit,
|
|
14
|
+
AsyncToolkit,
|
|
15
|
+
BaseToolkit,
|
|
16
|
+
ContextToolkit,
|
|
17
|
+
Toolkit,
|
|
18
|
+
)
|
|
19
|
+
from .tools import AsyncContextTool, AsyncTool, ContextTool, Tool
|
|
20
|
+
|
|
21
|
+
AnyTools: TypeAlias = (
|
|
22
|
+
Sequence[AnyToolSchema | ProviderTool] | BaseToolkit[AnyToolSchema]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
Tools: TypeAlias = Sequence[Tool | ProviderTool] | Toolkit
|
|
26
|
+
"""Type alias for sync tool parameters: a sequence of Tools or a Toolkit."""
|
|
27
|
+
|
|
28
|
+
AsyncTools: TypeAlias = Sequence[AsyncTool | ProviderTool] | AsyncToolkit
|
|
29
|
+
"""Type alias for async tool parameters: a sequence of AsyncTools or an AsyncToolkit."""
|
|
30
|
+
|
|
31
|
+
ContextTools = TypeAliasType(
|
|
32
|
+
"ContextTools",
|
|
33
|
+
Sequence[Tool | ContextTool[DepsT] | ProviderTool] | ContextToolkit[DepsT],
|
|
34
|
+
type_params=(DepsT,),
|
|
35
|
+
)
|
|
36
|
+
"""Type alias for sync context tool parameters: a sequence of Tools/ContextTools or a ContextToolkit."""
|
|
37
|
+
|
|
38
|
+
AsyncContextTools = TypeAliasType(
|
|
39
|
+
"AsyncContextTools",
|
|
40
|
+
Sequence[AsyncTool | AsyncContextTool[DepsT] | ProviderTool]
|
|
41
|
+
| AsyncContextToolkit[DepsT],
|
|
42
|
+
type_params=(DepsT,),
|
|
43
|
+
)
|
|
44
|
+
"""Type alias for async context tool parameters: a sequence of AsyncTools/AsyncContextTools or an AsyncContextToolkit."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def normalize_tools(tools: Tools | None) -> Toolkit:
|
|
48
|
+
"""Normalize tools input to a Toolkit.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
tools: A sequence of Tools, a Toolkit, or None.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
A Toolkit containing the tools (or an empty Toolkit if None).
|
|
55
|
+
"""
|
|
56
|
+
if tools is None:
|
|
57
|
+
return Toolkit(None)
|
|
58
|
+
if isinstance(tools, Toolkit):
|
|
59
|
+
return tools
|
|
60
|
+
return Toolkit(tools)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def normalize_async_tools(tools: AsyncTools | None) -> AsyncToolkit:
|
|
64
|
+
"""Normalize async tools input to an AsyncToolkit.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
tools: A sequence of AsyncTools, an AsyncToolkit, or None.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
An AsyncToolkit containing the tools (or an empty AsyncToolkit if None).
|
|
71
|
+
"""
|
|
72
|
+
if tools is None:
|
|
73
|
+
return AsyncToolkit(None)
|
|
74
|
+
if isinstance(tools, AsyncToolkit):
|
|
75
|
+
return tools
|
|
76
|
+
return AsyncToolkit(tools)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def normalize_context_tools(
|
|
80
|
+
tools: ContextTools[DepsT] | None,
|
|
81
|
+
) -> ContextToolkit[DepsT]:
|
|
82
|
+
"""Normalize context tools input to a ContextToolkit.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
tools: A sequence of Tools/ContextTools, a ContextToolkit, or None.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
A ContextToolkit containing the tools (or an empty ContextToolkit if None).
|
|
89
|
+
"""
|
|
90
|
+
if tools is None:
|
|
91
|
+
return ContextToolkit(None)
|
|
92
|
+
if isinstance(tools, ContextToolkit):
|
|
93
|
+
return tools
|
|
94
|
+
return ContextToolkit(tools)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def normalize_async_context_tools(
|
|
98
|
+
tools: AsyncContextTools[DepsT] | None,
|
|
99
|
+
) -> AsyncContextToolkit[DepsT]:
|
|
100
|
+
"""Normalize async context tools input to an AsyncContextToolkit.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
tools: A sequence of AsyncTools/AsyncContextTools, an AsyncContextToolkit, or None.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
An AsyncContextToolkit containing the tools (or an empty AsyncContextToolkit if None).
|
|
107
|
+
"""
|
|
108
|
+
if tools is None:
|
|
109
|
+
return AsyncContextToolkit(None)
|
|
110
|
+
if isinstance(tools, AsyncContextToolkit):
|
|
111
|
+
return tools
|
|
112
|
+
return AsyncContextToolkit(tools)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Web search tool for provider-native web search capabilities."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from .provider_tools import ProviderTool
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class WebSearchTool(ProviderTool):
|
|
10
|
+
"""Web search tool that allows the model to search the internet.
|
|
11
|
+
|
|
12
|
+
This is a provider tool - the search is executed server-side by the provider,
|
|
13
|
+
not by your code. The model decides when to search based on the prompt,
|
|
14
|
+
and the provider returns search results with citations.
|
|
15
|
+
|
|
16
|
+
Supported providers include Anthropic, Google, and OpenAI (when using the Responses API).
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
```python
|
|
20
|
+
from mirascope import llm
|
|
21
|
+
|
|
22
|
+
@llm.call("anthropic/claude-sonnet-4-5", tools=[llm.WebSearchTool()])
|
|
23
|
+
def search_web(query: str) -> str:
|
|
24
|
+
return f"Search the web for: {query}"
|
|
25
|
+
|
|
26
|
+
response = search_web("Who won the 2024 Super Bowl?")
|
|
27
|
+
print(response.text()) # Response includes citations from web search
|
|
28
|
+
```
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
name: str = field(default="web_search", init=False)
|
|
32
|
+
"""The tool name. Always "web_search" for this tool type."""
|
mirascope/ops/__init__.py
CHANGED
|
@@ -12,9 +12,18 @@ stub_module_if_missing("mirascope.ops", "ops")
|
|
|
12
12
|
# ruff: noqa: E402
|
|
13
13
|
from ._internal.configuration import configure, tracer_context
|
|
14
14
|
from ._internal.context import propagated_context
|
|
15
|
-
from ._internal.instrumentation
|
|
15
|
+
from ._internal.instrumentation import (
|
|
16
|
+
instrument_anthropic,
|
|
17
|
+
instrument_google_genai,
|
|
16
18
|
instrument_llm,
|
|
19
|
+
instrument_openai,
|
|
20
|
+
is_anthropic_instrumented,
|
|
21
|
+
is_google_genai_instrumented,
|
|
22
|
+
is_openai_instrumented,
|
|
23
|
+
uninstrument_anthropic,
|
|
24
|
+
uninstrument_google_genai,
|
|
17
25
|
uninstrument_llm,
|
|
26
|
+
uninstrument_openai,
|
|
18
27
|
)
|
|
19
28
|
from ._internal.propagation import (
|
|
20
29
|
ContextPropagator,
|
|
@@ -99,13 +108,22 @@ __all__ = [
|
|
|
99
108
|
"extract_session_id",
|
|
100
109
|
"get_propagator",
|
|
101
110
|
"inject_context",
|
|
111
|
+
"instrument_anthropic",
|
|
112
|
+
"instrument_google_genai",
|
|
102
113
|
"instrument_llm",
|
|
114
|
+
"instrument_openai",
|
|
115
|
+
"is_anthropic_instrumented",
|
|
116
|
+
"is_google_genai_instrumented",
|
|
117
|
+
"is_openai_instrumented",
|
|
103
118
|
"propagated_context",
|
|
104
119
|
"reset_propagator",
|
|
105
120
|
"session",
|
|
106
121
|
"span",
|
|
107
122
|
"trace",
|
|
108
123
|
"tracer_context",
|
|
124
|
+
"uninstrument_anthropic",
|
|
125
|
+
"uninstrument_google_genai",
|
|
109
126
|
"uninstrument_llm",
|
|
127
|
+
"uninstrument_openai",
|
|
110
128
|
"version",
|
|
111
129
|
]
|
|
@@ -738,12 +738,15 @@ class _DependencyCollector:
|
|
|
738
738
|
# For Python 3.13+
|
|
739
739
|
return definition.func # pyright: ignore[reportFunctionMemberAccess] # pragma: no cover
|
|
740
740
|
|
|
741
|
+
# Handle objects with .fn but no __qualname__ (e.g., old-style wrappers).
|
|
742
|
+
# With copy_function_metadata() now copying __qualname__ to ToolSchema, Prompt,
|
|
743
|
+
# Call, etc., this branch is no longer reached in normal usage.
|
|
741
744
|
if (
|
|
742
745
|
(wrapped_function := getattr(definition, "fn", None)) is not None
|
|
743
746
|
and not hasattr(definition, "__qualname__")
|
|
744
747
|
and callable(wrapped_function)
|
|
745
748
|
):
|
|
746
|
-
return wrapped_function
|
|
749
|
+
return wrapped_function # pragma: no cover
|
|
747
750
|
|
|
748
751
|
return definition
|
|
749
752
|
|
|
@@ -20,14 +20,17 @@ from ....api._generated.traces.types import (
|
|
|
20
20
|
TracesCreateRequestResourceSpansItemResource,
|
|
21
21
|
TracesCreateRequestResourceSpansItemResourceAttributesItem,
|
|
22
22
|
TracesCreateRequestResourceSpansItemResourceAttributesItemValue,
|
|
23
|
+
TracesCreateRequestResourceSpansItemResourceAttributesItemValueArrayValue,
|
|
23
24
|
TracesCreateRequestResourceSpansItemScopeSpansItem,
|
|
24
25
|
TracesCreateRequestResourceSpansItemScopeSpansItemScope,
|
|
25
26
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItem,
|
|
26
27
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItem,
|
|
27
28
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue,
|
|
29
|
+
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValueArrayValue,
|
|
28
30
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemStatus,
|
|
29
31
|
)
|
|
30
32
|
from ....api.client import Mirascope
|
|
33
|
+
from .utils import to_otlp_any_value
|
|
31
34
|
|
|
32
35
|
logger = logging.getLogger(__name__)
|
|
33
36
|
|
|
@@ -282,17 +285,7 @@ class MirascopeOTLPExporter(SpanExporter):
|
|
|
282
285
|
def _convert_attribute_value(
|
|
283
286
|
self, value: AttributeValue
|
|
284
287
|
) -> TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue:
|
|
285
|
-
"""Convert OpenTelemetry AttributeValue to Mirascope API's KeyValueValue.
|
|
286
|
-
|
|
287
|
-
This conversion is necessary because the Fern-generated API client
|
|
288
|
-
expects KeyValueValue objects, not OpenTelemetry's AttributeValue types.
|
|
289
|
-
|
|
290
|
-
Args:
|
|
291
|
-
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
292
|
-
|
|
293
|
-
Returns:
|
|
294
|
-
A KeyValueValue object for the Mirascope API
|
|
295
|
-
"""
|
|
288
|
+
"""Convert OpenTelemetry AttributeValue to Mirascope API's KeyValueValue."""
|
|
296
289
|
match value:
|
|
297
290
|
case str():
|
|
298
291
|
return TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue(
|
|
@@ -312,49 +305,21 @@ class MirascopeOTLPExporter(SpanExporter):
|
|
|
312
305
|
)
|
|
313
306
|
case _:
|
|
314
307
|
return TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue(
|
|
315
|
-
|
|
308
|
+
array_value=TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValueArrayValue(
|
|
309
|
+
values=[to_otlp_any_value(v) for v in value]
|
|
310
|
+
)
|
|
316
311
|
)
|
|
317
312
|
|
|
318
313
|
def _convert_event_attribute_value(
|
|
319
314
|
self, value: AttributeValue
|
|
320
315
|
) -> dict[str, object]:
|
|
321
|
-
"""Convert OpenTelemetry AttributeValue to OTLP event attribute value format.
|
|
322
|
-
|
|
323
|
-
This uses the OTLP JSON format with typed value wrappers (e.g., stringValue, intValue).
|
|
324
|
-
|
|
325
|
-
Args:
|
|
326
|
-
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
327
|
-
|
|
328
|
-
Returns:
|
|
329
|
-
A dict with the typed value (e.g., {"stringValue": "..."})
|
|
330
|
-
"""
|
|
331
|
-
match value:
|
|
332
|
-
case str():
|
|
333
|
-
return {"stringValue": value}
|
|
334
|
-
case bool():
|
|
335
|
-
return {"boolValue": value}
|
|
336
|
-
case int():
|
|
337
|
-
return {"intValue": str(value)}
|
|
338
|
-
case float():
|
|
339
|
-
return {"doubleValue": value}
|
|
340
|
-
case _:
|
|
341
|
-
# Sequences - convert to string representation
|
|
342
|
-
return {"stringValue": str(list(value))}
|
|
316
|
+
"""Convert OpenTelemetry AttributeValue to OTLP event attribute value format."""
|
|
317
|
+
return to_otlp_any_value(value)
|
|
343
318
|
|
|
344
319
|
def _convert_resource_attribute_value(
|
|
345
320
|
self, value: AttributeValue
|
|
346
321
|
) -> TracesCreateRequestResourceSpansItemResourceAttributesItemValue:
|
|
347
|
-
"""Convert OpenTelemetry AttributeValue to Mirascope API's resource KeyValueValue.
|
|
348
|
-
|
|
349
|
-
This conversion is necessary because the Fern-generated API client
|
|
350
|
-
expects KeyValueValue objects, not OpenTelemetry's AttributeValue types.
|
|
351
|
-
|
|
352
|
-
Args:
|
|
353
|
-
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
354
|
-
|
|
355
|
-
Returns:
|
|
356
|
-
A KeyValueValue object for the Mirascope API resource attributes
|
|
357
|
-
"""
|
|
322
|
+
"""Convert OpenTelemetry AttributeValue to Mirascope API's resource KeyValueValue."""
|
|
358
323
|
match value:
|
|
359
324
|
case str():
|
|
360
325
|
return TracesCreateRequestResourceSpansItemResourceAttributesItemValue(
|
|
@@ -374,7 +339,9 @@ class MirascopeOTLPExporter(SpanExporter):
|
|
|
374
339
|
)
|
|
375
340
|
case _:
|
|
376
341
|
return TracesCreateRequestResourceSpansItemResourceAttributesItemValue(
|
|
377
|
-
|
|
342
|
+
array_value=TracesCreateRequestResourceSpansItemResourceAttributesItemValueArrayValue(
|
|
343
|
+
values=[to_otlp_any_value(v) for v in value]
|
|
344
|
+
)
|
|
378
345
|
)
|
|
379
346
|
|
|
380
347
|
def shutdown(self) -> None:
|
|
@@ -4,6 +4,43 @@ This module provides helper functions for formatting and converting
|
|
|
4
4
|
OpenTelemetry data types for export.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping, Sequence
|
|
10
|
+
|
|
11
|
+
from opentelemetry.util.types import AttributeValue
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def to_otlp_any_value(value: AttributeValue) -> dict[str, object]:
|
|
15
|
+
"""Convert AttributeValue to OTLP AnyValue (dict form).
|
|
16
|
+
|
|
17
|
+
- string/bool/int/float are converted to stringValue/boolValue/intValue/doubleValue
|
|
18
|
+
- Sequence (excluding str/bytes/Mapping) is converted to arrayValue.values
|
|
19
|
+
- Unsupported types fallback to stringValue=str(value)
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A dict representing OTLP AnyValue (e.g., {"stringValue": "..."})
|
|
26
|
+
"""
|
|
27
|
+
match value:
|
|
28
|
+
case str():
|
|
29
|
+
return {"stringValue": value}
|
|
30
|
+
case bool():
|
|
31
|
+
return {"boolValue": value}
|
|
32
|
+
case int():
|
|
33
|
+
return {"intValue": str(value)}
|
|
34
|
+
case float():
|
|
35
|
+
return {"doubleValue": value}
|
|
36
|
+
case _ if isinstance(value, bytes | bytearray | memoryview | Mapping):
|
|
37
|
+
return {"stringValue": str(value)}
|
|
38
|
+
case _ if isinstance(value, Sequence):
|
|
39
|
+
values = [to_otlp_any_value(v) for v in value]
|
|
40
|
+
return {"arrayValue": {"values": values}}
|
|
41
|
+
case _:
|
|
42
|
+
return {"stringValue": str(value)}
|
|
43
|
+
|
|
7
44
|
|
|
8
45
|
def format_trace_id(trace_id: int) -> str:
|
|
9
46
|
"""Format a trace ID as a 32-character hex string.
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
"""Instrumentation modules for various frameworks and libraries."""
|
|
2
2
|
|
|
3
3
|
from .llm import instrument_llm, uninstrument_llm
|
|
4
|
+
from .providers import (
|
|
5
|
+
instrument_anthropic,
|
|
6
|
+
instrument_google_genai,
|
|
7
|
+
instrument_openai,
|
|
8
|
+
is_anthropic_instrumented,
|
|
9
|
+
is_google_genai_instrumented,
|
|
10
|
+
is_openai_instrumented,
|
|
11
|
+
uninstrument_anthropic,
|
|
12
|
+
uninstrument_google_genai,
|
|
13
|
+
uninstrument_openai,
|
|
14
|
+
)
|
|
4
15
|
|
|
5
16
|
__all__ = [
|
|
17
|
+
"instrument_anthropic",
|
|
18
|
+
"instrument_google_genai",
|
|
6
19
|
"instrument_llm",
|
|
20
|
+
"instrument_openai",
|
|
21
|
+
"is_anthropic_instrumented",
|
|
22
|
+
"is_google_genai_instrumented",
|
|
23
|
+
"is_openai_instrumented",
|
|
24
|
+
"uninstrument_anthropic",
|
|
25
|
+
"uninstrument_google_genai",
|
|
7
26
|
"uninstrument_llm",
|
|
27
|
+
"uninstrument_openai",
|
|
8
28
|
]
|