Skip to content

Commit e31fa18

Browse files
authored
Merge pull request #905 from Mirascope/fix-902
Fix 902 and 903
2 parents b2c79bd + 3e78475 commit e31fa18

File tree

5 files changed

+80
-36
lines changed

5 files changed

+80
-36
lines changed

docs/CONTRIBUTING.md

+9
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ A few key things to note:
155155
2. The API reference is generated automatically, but things like new modules still need to be included in the `docs/api` structure for generation to work.
156156
3. The `docs/tutorials` are written as Jupyter notebooks that get converted into markdown. This conversion will happen on every save when running the server locally, which can make writing docs slow. We recommend setting `strict: false` and commenting out the `mkdocs-jupyter` plugin in `mkdocs.yml` to skip the conversion.
157157

158+
!!! tip "MacOS Users Will Need `cairo`
159+
160+
If you're developing using MacOS, you'll need to follow these steps:
161+
162+
1. `brew install cairo`
163+
2. `export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH"`
164+
3. `export DYLD_LIBRARY_PATH="/usr/local/lib:/opt/homebrew/lib:$DYLD_LIBRARY_PATH"`
165+
4. `source ~/.bash_profile`
166+
158167
## Testing
159168
160169
All changes to the codebase must be properly unit tested. If a change requires updating an existing unit test, make sure to think through if the change is breaking.

mirascope/core/google/tool.py

+43-32
Original file line numberDiff line numberDiff line change
@@ -70,48 +70,58 @@ def format_book(title: str, author: str) -> str:
7070
fn["parameters"] = model_schema
7171

7272
if "parameters" in fn:
73-
# Resolve $defs and $ref
73+
# Define a function to handle both ref resolution and examples conversion
74+
def resolve_refs_and_fix_examples(
75+
schema: dict[str, Any], defs: dict[str, Any] | None = None
76+
) -> dict[str, Any]:
77+
"""Recursively resolve $ref references and fix examples/example fields."""
78+
# If this is a reference, resolve it
79+
if "$ref" in schema:
80+
ref = schema["$ref"]
81+
if ref.startswith("#/$defs/") and defs:
82+
ref_key = ref.replace("#/$defs/", "")
83+
if ref_key in defs:
84+
# Merge the definition with the current schema (excluding $ref)
85+
resolved = {
86+
**{k: v for k, v in schema.items() if k != "$ref"},
87+
**resolve_refs_and_fix_examples(defs[ref_key], defs),
88+
}
89+
return resolved
90+
91+
# Handle examples -> example conversion
92+
result = {}
93+
for key, value in schema.items():
94+
# Convert "examples" to "example" for Google Schema
95+
if key == "examples":
96+
result["example"] = value
97+
elif isinstance(value, dict):
98+
result[key] = resolve_refs_and_fix_examples(value, defs)
99+
elif isinstance(value, list):
100+
result[key] = [
101+
resolve_refs_and_fix_examples(item, defs)
102+
if isinstance(item, dict)
103+
else item
104+
for item in value
105+
]
106+
else:
107+
result[key] = value
108+
return result
109+
110+
# Extract $defs if they exist
111+
defs = {}
74112
if "$defs" in fn["parameters"]:
75113
defs = fn["parameters"].pop("$defs")
76114

77-
def resolve_refs(schema: dict[str, Any]) -> dict[str, Any]:
78-
"""Recursively resolve $ref references using the $defs dictionary."""
79-
# If this is a reference, resolve it
80-
if "$ref" in schema:
81-
ref = schema["$ref"]
82-
if ref.startswith("#/$defs/"):
83-
ref_key = ref.replace("#/$defs/", "")
84-
if ref_key in defs:
85-
# Merge the definition with the current schema (excluding $ref)
86-
resolved = {
87-
**{k: v for k, v in schema.items() if k != "$ref"},
88-
**resolve_refs(defs[ref_key]),
89-
}
90-
return resolved
91-
92-
# Process all other keys recursively
93-
result = {}
94-
for key, value in schema.items():
95-
if isinstance(value, dict):
96-
result[key] = resolve_refs(value)
97-
elif isinstance(value, list):
98-
result[key] = [
99-
resolve_refs(item) if isinstance(item, dict) else item
100-
for item in value
101-
]
102-
else:
103-
result[key] = value
104-
return result
105-
106-
# Resolve all references in the parameters
107-
fn["parameters"] = resolve_refs(fn["parameters"])
115+
# Resolve all references in the parameters and fix examples
116+
fn["parameters"] = resolve_refs_and_fix_examples(fn["parameters"], defs)
108117

109118
def handle_enum_schema(prop_schema: dict[str, Any]) -> dict[str, Any]:
110119
if "enum" in prop_schema:
111120
prop_schema["format"] = "enum"
112121
return prop_schema
113122

114123
# Process properties after resolving references
124+
# We're already handling examples -> example conversion recursively above
115125
fn["parameters"]["properties"] = {
116126
prop: {
117127
key: value
@@ -120,6 +130,7 @@ def handle_enum_schema(prop_schema: dict[str, Any]) -> dict[str, Any]:
120130
}
121131
for prop, prop_schema in fn["parameters"]["properties"].items()
122132
}
133+
123134
return Tool(function_declarations=[FunctionDeclaration(**fn)])
124135

125136
@classmethod

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mirascope"
3-
version = "1.21.1"
3+
version = "1.21.2"
44
description = "LLM abstractions that aren't obstructions"
55
readme = "README.md"
66
license = { file = "LICENSE" }

tests/core/google/test_tool.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Tool,
1111
Type,
1212
)
13+
from pydantic import Field
1314

1415
from mirascope.core.base.tool import BaseTool
1516
from mirascope.core.google.tool import GoogleTool
@@ -18,10 +19,23 @@
1819
class FormatBook(GoogleTool):
1920
"""Returns the title and author nicely formatted."""
2021

21-
title: str
22+
title: str = Field(..., examples=["The Name of the Wind"])
2223
author: str
2324
genre: Literal["fantasy", "scifi"]
2425

26+
model_config = {
27+
"json_schema_extra": {
28+
"examples": [
29+
{
30+
"title": "The Way of Kings",
31+
"author": "Brandon Sanderson",
32+
"genre": "fantasy",
33+
},
34+
{"title": "Dune", "author": "Frank Herbert", "genre": "scifi"},
35+
]
36+
}
37+
}
38+
2539
def call(self) -> str:
2640
return f"{self.title} by {self.author}"
2741

@@ -54,7 +68,9 @@ def test_google_tool() -> None:
5468
parameters=Schema(
5569
type=Type.OBJECT,
5670
properties={
57-
"title": Schema(type=Type.STRING),
71+
"title": Schema(
72+
type=Type.STRING, example=["The Name of the Wind"]
73+
),
5874
"author": Schema(type=Type.STRING),
5975
"genre": Schema(
6076
type=Type.STRING,
@@ -63,6 +79,14 @@ def test_google_tool() -> None:
6379
),
6480
},
6581
required=["title", "author", "genre"],
82+
example=[
83+
{
84+
"title": "The Way of Kings",
85+
"author": "Brandon Sanderson",
86+
"genre": "fantasy",
87+
},
88+
{"title": "Dune", "author": "Frank Herbert", "genre": "scifi"},
89+
],
6690
),
6791
)
6892
],

uv.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)