-
Notifications
You must be signed in to change notification settings - Fork 323
Description
Describe the bug
I don't know is this intended behavior not to use optional field for response model. but it seems bug since genkit for node.js supports optional field.
If the response model includes nullable field. an error occurs while converting the model to supports OpenAI spec in following function.
google.genai._transformers.process_schema._recurse
def _recurse(sub_schema: dict[str, Any]) -> dict[str, Any]:
"""Returns the processed `sub_schema`, resolving its '$ref' if any."""
if (ref := sub_schema.pop('$ref', None)) is not None: ## <--- if sub_schema is None then "AttributeError: 'NoneType' object has no attribute 'pop'" occured.
sub_schema = defs[ref.split('defs/')[-1]]
process_schema(sub_schema, client, defs, order_properties=order_properties)
return sub_schema
because below function changes the Optional field into None which the type is anyOf
genkit.plugins.google_genai.models.gemini.GenimiModel._convert_schema_property
def _convert_schema_property(
self, input_schema: dict[str, Any], defs: dict[str, Any] | None = None
) -> genai_types.Schema | None:
"""Sanitizes a schema to be compatible with Gemini API.
Args:
input_schema: A dictionary with input parameters
defs: Dictionary with definitions. Optional.
Returns:
Schema or None
"""
if input_schema is None or 'type' not in input_schema: # < --- this line changes the filed to None
return None
if defs is None:
defs = input_schema.get('$defs') if '$defs' in input_schema else {}
schema = genai_types.Schema()
if input_schema.get('description'):
schema.description = input_schema['description']
if 'required' in input_schema:
schema.required = input_schema['required']
if 'type' in input_schema:
schema_type = genai_types.Type(input_schema['type'])
schema.type = schema_type
if 'enum' in input_schema:
schema.enum = input_schema['enum']
if schema_type == genai_types.Type.ARRAY:
schema.items = self._convert_schema_property(input_schema['items'], defs)
if schema_type == genai_types.Type.OBJECT:
schema.properties = {}
properties = input_schema['properties']
for key in properties:
if isinstance(properties[key], dict) and '$ref' in properties[key]:
ref_tokens = properties[key]['$ref'].split('/')
if ref_tokens[2] not in defs:
raise ValueError(f'Failed to resolve schema for {ref_tokens[2]}')
resolved_schema = self._convert_schema_property(defs[ref_tokens[2]], defs)
schema.properties[key] = resolved_schema
if 'description' in properties[key]:
schema.properties[key].description = properties[key]['description']
else:
nested_schema = self._convert_schema_property(properties[key], defs)
schema.properties[key] = nested_schema
return schema
To Reproduce
Give the response model includes nullable Field for chat completion that returns structured output.
Expected behavior
The nullable field successfully converted to OpenAI supports field by handle_null_fields
function.
Screenshots
If applicable, add screenshots to help explain your problem.
Runtime (please complete the following information):
- OS: Apple M1 Sequoia 15.4.1
- "genkit>=0.4.0",
- "genkit-plugin-google-genai>=0.4.0",
** Python version
- Python 3.12.9
Additional context
import json
from pydantic import BaseModel, Field
from genkit.ai import Genkit
from genkit.plugins.google_genai import GoogleAI
from typing import Optional
from models.eng_kor_dictionary import DictionaryEntry
from utils.dictionary import get_lemmatized_word
ai = Genkit(
plugins=[GoogleAI()],
model='googleai/gemini-2.0-flash',
)
class RpgCharacter(BaseModel):
name: str = Field(description='name of the character')
back_story: str = Field(description='back story')
abilities: Optional[list[str]] = Field(description='list of abilities (3-4)')
@ai.flow()
async def generate_character(name: str):
result = await ai.generate(
prompt=f'generate an RPG character named {name}',
output_schema=RpgCharacter,
)
return result.output
async def main() -> None:
print(json.dumps(await generate_character('Goblorb'), indent=2))
(-) (base) ---@--- functions % cd /Users/---/---/---/functions ; /usr/bin/env /Users/---/
.venv/bin/python /Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bu
ndled/libs/debugpy/adapter/../../debugpy/launcher 53232 -- ai_app.py
Traceback (most recent call last):
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/core/action/_action.py", line 519, in async_tracing_wrapper
output = await afn(input, ctx)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/plugins/google_genai/models/gemini.py", line 683, in generate
response = await self._generate(
^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/plugins/google_genai/models/gemini.py", line 719, in _generate
response = await self._client.aio.models.generate_content(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/models.py", line 7124, in generate_content
response = await self._generate_content(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/models.py", line 6094, in _generate_content
request_dict = _GenerateContentParameters_to_mldev(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/models.py", line 812, in _GenerateContentParameters_to_mldev
_GenerateContentConfig_to_mldev(
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/models.py", line 691, in _GenerateContentConfig_to_mldev
t.t_schema(api_client, getv(from_object, ['response_schema'])),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/_transformers.py", line 824, in t_schema
process_schema(schema, client)
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/_transformers.py", line 762, in process_schema
properties[name] = _recurse(sub_schema)
^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/google/genai/_transformers.py", line 735, in _recurse
if (ref := sub_schema.pop('$ref', None)) is not None:
^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'pop'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/core/action/_action.py", line 517, in async_tracing_wrapper
output = await afn(input)
^^^^^^^^^^^^^^^^
File "ai_app.py", line 22, in generate_character
result = await ai.generate(
^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/ai/_aio.py", line 162, in generate
return await generate_action(
^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/blocks/generate.py", line 231, in generate_action
model_response = await dispatch(
^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/blocks/generate.py", line 209, in dispatch
await model.arun(
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/core/action/_action.py", line 332, in arun
return await self._afn(
^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/core/action/_action.py", line 523, in async_tracing_wrapper
raise GenkitError(
genkit.core.error.GenkitError: None: Error while running action googleai/gemini-2.0-flash
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/local/Cellar/[email protected]/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/runpy.py", line 198, in _run_module_as_main
return _run_code(code, main_globals, None,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/Cellar/[email protected]/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/runpy.py", line 88, in _run_code
exec(code, run_globals)
File "/Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
cli.main()
File "/Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
run()
File "/Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
runpy.run_path(target, run_name="__main__")
File "/Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
return _run_module_code(code, init_globals, run_name,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "/Users/---/.cursor/extensions/ms-python.debugpy-2024.6.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
exec(code, run_globals)
File "ai_app.py", line 50, in <module>
ai.run_main(main())
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/ai/_base.py", line 89, in run_main
result = asyncio.run(coro)
^^^^^^^^^^^^^^^^^
File "/usr/local/Cellar/[email protected]/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 195, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/local/Cellar/[email protected]/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/Cellar/[email protected]/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "ai_app.py", line 29, in main
print(json.dumps(await generate_character('Goblorb'), indent=2))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/ai/_registry.py", line 148, in async_wrapper
return (await action.arun(*args, **kwargs)).response
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/core/action/_action.py", line 332, in arun
return await self._afn(
^^^^^^^^^^^^^^^^
File "/Users/---/.venv/lib/python3.12/site-packages/genkit/core/action/_action.py", line 523, in async_tracing_wrapper
raise GenkitError(
genkit.core.error.GenkitError: None: Error while running action generate_character
Metadata
Metadata
Assignees
Labels
Type
Projects
Status