Skip to content

feat: implement google genai provider #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: main
Choose a base branch
from

Conversation

monotykamary
Copy link

@monotykamary monotykamary commented Apr 30, 2025

This PR implements the Google provider for fast-agent using the native google.genai library and should resolve #6 .

Key changes include:

  • Integration with google.genai.Client for interacting with Google's generative models.
  • Implementation of conversation history conversion between fast-agent's PromptMessageMultipart and google.genai.types.Content.
  • Creation of a GoogleConverter class to handle data structure conversions.
  • Handling of tool calls and responses using the native Google API format.
Normal conversation Tool-use History + tool-use Images
Image 1 Image 2 Image 3 Image 4
Image test code sample
import asyncio
from mcp.types import ImageContent, TextContent
from mcp_agent.core.fastagent import FastAgent
from mcp_agent import RequestParams
from mcp_agent.core.prompt import Prompt
from pathlib import Path

fast = FastAgent("Image test")

@fast.agent(
    name="test",
    model="google.gemini-2.5-flash-preview-04-17",
)

async def main():
    async with fast.run() as agent:
      response: str = await agent.send(
          Prompt.user(
              "Analyze this image",
              Path('/Users/monotykamary/Downloads/dikw.png')
          )
      )

      print(response)

if __name__ == "__main__":
    asyncio.run(main())

This work is a step towards fully leveraging the features of the google.genai library within fast-agent.

@monotykamary monotykamary marked this pull request as draft April 30, 2025 08:03
@monotykamary
Copy link
Author

monotykamary commented Apr 30, 2025

Test summary
================================= test session starts =================================
platform darwin -- Python 3.12.7, pytest-8.3.5, pluggy-1.5.0 -- /Users/monotykamary/VCS/working-remote/open-source/fast-agent/.venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/monotykamary/VCS/working-remote/open-source/fast-agent
configfile: pyproject.toml
plugins: anyio-4.9.0, asyncio-0.26.0, cov-6.1.1
asyncio: mode=Mode.STRICT, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collected 199 items                                                                   

tests/unit/mcp_agent/agents/test_agent_types.py::test_agent_type_default PASSED [  0%]
tests/unit/mcp_agent/agents/workflow/test_orchestrator_agent.py::test_execute_step PASSED [  1%]
tests/unit/mcp_agent/agents/workflow/test_orchestrator_agent.py::test_invalid_agent_handling PASSED [  1%]
tests/unit/mcp_agent/agents/workflow/test_orchestrator_agent.py::test_plan_execution_flow PASSED [  2%]
tests/unit/mcp_agent/agents/workflow/test_orchestrator_agent.py::test_iterative_planning PASSED [  2%]
tests/unit/mcp_agent/agents/workflow/test_router_unit.py::test_routing_response_model PASSED [  3%]
tests/unit/mcp_agent/agents/workflow/test_router_unit.py::test_disallows_empty_agents PASSED [  3%]
tests/unit/mcp_agent/agents/workflow/test_router_unit.py::test_invalid_llm_response PASSED [  4%]
tests/unit/mcp_agent/agents/workflow/test_router_unit.py::test_single_agent_shortcircuit PASSED [  4%]
tests/unit/mcp_agent/core/test_mcp_content.py::test_text_content PASSED         [  5%]
tests/unit/mcp_agent/core/test_mcp_content.py::test_image_content PASSED        [  5%]
tests/unit/mcp_agent/core/test_mcp_content.py::test_resource_content PASSED     [  6%]
tests/unit/mcp_agent/core/test_mcp_content.py::test_prompt_function PASSED      [  6%]
tests/unit/mcp_agent/core/test_mcp_content.py::test_user_assistant_functions PASSED [  7%]
tests/unit/mcp_agent/core/test_prompt.py::test_user_method PASSED               [  7%]
tests/unit/mcp_agent/core/test_prompt.py::test_assistant_method PASSED          [  8%]
tests/unit/mcp_agent/core/test_prompt.py::test_message_method PASSED            [  8%]
tests/unit/mcp_agent/core/test_prompt.py::test_with_file_paths PASSED           [  9%]
tests/unit/mcp_agent/core/test_prompt.py::test_conversation_method PASSED       [  9%]
tests/unit/mcp_agent/core/test_prompt.py::test_from_multipart_method PASSED     [ 10%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_assistant_role_restrictions PASSED [ 10%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_code_file_as_text_document_with_filename PASSED [ 11%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_code_file_as_text_document_with_uri PASSED [ 11%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_embedded_resource_image_url_conversion PASSED [ 12%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_embedded_resource_pdf_conversion PASSED [ 12%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_embedded_resource_pdf_url_conversion PASSED [ 13%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_embedded_resource_text_conversion PASSED [ 13%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_empty_content_list PASSED [ 14%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_image_content_conversion PASSED [ 14%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_mixed_content_with_unsupported_formats PASSED [ 15%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_multiple_content_blocks PASSED [ 15%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_svg_resource_conversion PASSED [ 16%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_text_content_conversion PASSED [ 16%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_unsupported_binary_resource_conversion PASSED [ 17%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicUserConverter::test_unsupported_mime_type_handling PASSED [ 17%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_binary_only_tool_result_conversion PASSED [ 18%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_create_tool_results_message PASSED [ 18%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_empty_tool_result_conversion PASSED [ 19%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_error_tool_result_conversion PASSED [ 19%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_image_tool_result_conversion PASSED [ 20%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_mixed_tool_markdown_result_conversion PASSED [ 20%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_mixed_tool_result_conversion PASSED [ 21%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_pdf_result_conversion PASSED [ 21%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_text_tool_result_conversion PASSED [ 22%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicToolConverter::test_unsupported_image_format_in_tool_result PASSED [ 22%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_assistant_embedded_resource_stripped PASSED [ 23%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_assistant_empty_content PASSED [ 23%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_assistant_multiple_text_blocks PASSED [ 24%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_assistant_non_text_content_stripped PASSED [ 24%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_assistant_text_content_conversion PASSED [ 25%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_convert_prompt_message_embedded_resource_to_anthropic PASSED [ 25%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_convert_prompt_message_image_to_anthropic PASSED [ 26%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_anthropic.py::TestAnthropicAssistantConverter::test_convert_prompt_message_to_anthropic PASSED [ 26%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_code_file_conversion PASSED [ 27%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_embedded_resource_image_url_conversion PASSED [ 27%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_embedded_resource_pdf_conversion PASSED [ 28%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_embedded_resource_text_conversion PASSED [ 28%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_empty_content_list PASSED [ 29%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_image_content_conversion PASSED [ 29%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_multiple_content_blocks PASSED [ 30%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_svg_resource_conversion PASSED [ 30%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIUserConverter::test_text_content_conversion PASSED [ 31%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIAssistantConverter::test_assistant_text_content_conversion PASSED [ 31%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIAssistantConverter::test_convert_prompt_message_embedded_resource_to_openai PASSED [ 32%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIAssistantConverter::test_convert_prompt_message_to_openai_assistant PASSED [ 32%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIAssistantConverter::test_convert_prompt_message_to_openai_user_image PASSED [ 33%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIAssistantConverter::test_convert_prompt_message_to_openai_user_text PASSED [ 33%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIAssistantConverter::test_empty_assistant_message PASSED [ 34%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIToolConverter::test_multiple_tool_results_with_mixed_content PASSED [ 34%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIToolConverter::test_tool_result_conversion PASSED [ 35%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestOpenAIToolConverter::test_tool_result_with_mixed_content PASSED [ 35%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestTextConcatenation::test_adjacent_text_blocks_concatenation PASSED [ 36%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestTextConcatenation::test_convert_unsupported_binary_format PASSED [ 36%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestTextConcatenation::test_mixed_content_with_concatenation PASSED [ 37%]
tests/unit/mcp_agent/llm/providers/test_multipart_converter_openai.py::TestTextConcatenation::test_tool_result_with_concatenation PASSED [ 37%]
tests/unit/mcp_agent/llm/providers/test_sampling_converter_anthropic.py::TestAnthropicMCPTypeConverter::test_stop_reason_conversions PASSED [ 38%]
tests/unit/mcp_agent/llm/providers/test_sampling_converter_openai.py::TestOpenAIMCPTypeConverter::test_from_mcp_prompt_message_user PASSED [ 38%]
tests/unit/mcp_agent/llm/providers/test_sampling_converter_openai.py::TestOpenAIMCPTypeConverter::test_from_mcp_prompt_message_assistant PASSED [ 39%]
tests/unit/mcp_agent/llm/test_model_factory.py::test_simple_model_names PASSED  [ 39%]
tests/unit/mcp_agent/llm/test_model_factory.py::test_full_model_strings PASSED  [ 40%]
tests/unit/mcp_agent/llm/test_model_factory.py::test_invalid_inputs PASSED      [ 40%]
tests/unit/mcp_agent/llm/test_model_factory.py::test_llm_class_creation PASSED  [ 41%]
tests/unit/mcp_agent/llm/test_model_factory.py::test_allows_generic_model PASSED [ 41%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_simple_return PASSED         [ 42%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_concatenates_text_for_multiple_parts PASSED [ 42%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_set_fixed_return PASSED      [ 43%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_set_fixed_return_ignores_not_set PASSED [ 43%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_parse_tool_call_no_args PASSED [ 44%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_parse_tool_call_with_args PASSED [ 44%]
tests/unit/mcp_agent/llm/test_passthrough.py::test_generates_structured PASSED  [ 45%]
tests/unit/mcp_agent/llm/test_playback.py::test_model_factory_creates_playback PASSED [ 45%]
tests/unit/mcp_agent/llm/test_playback.py::test_basic_playback_function PASSED  [ 46%]
tests/unit/mcp_agent/llm/test_playback.py::test_simple_playback_functionality PASSED [ 46%]
tests/unit/mcp_agent/llm/test_playback.py::test_exhaustion_behaviour PASSED     [ 47%]
tests/unit/mcp_agent/llm/test_playback.py::test_cannot_load_history_with_structured PASSED [ 47%]
tests/unit/mcp_agent/llm/test_playback.py::test_generates_structured PASSED     [ 48%]
tests/unit/mcp_agent/llm/test_playback.py::test_generates_structured_exhaustion_behaves PASSED [ 48%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_base_prepare_provider_arguments PASSED [ 49%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_prepare_arguments_with_exclusions PASSED [ 49%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_prepare_arguments_with_metadata PASSED [ 50%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_response_format_handling PASSED [ 50%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_openai_provider_arguments PASSED [ 51%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_anthropic_provider_arguments PASSED [ 51%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_params_dont_overwrite_base_args PASSED [ 52%]
tests/unit/mcp_agent/llm/test_prepare_arguments.py::TestRequestParamsInLLM::test_none_values_not_included PASSED [ 52%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_sampling_message_to_prompt_message_text PASSED [ 53%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_sampling_message_to_prompt_message_image PASSED [ 53%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_convert_messages PASSED [ 54%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_convert_messages_with_mixed_content_types PASSED [ 54%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_extract_request_params_full PASSED [ 55%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_extract_request_params_minimal PASSED [ 55%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_error_result PASSED [ 56%]
tests/unit/mcp_agent/llm/test_sampling_converter.py::TestSamplingConverter::test_error_result_no_model PASSED [ 56%]
tests/unit/mcp_agent/llm/test_structured.py::test_direct_pydantic_conversion PASSED [ 57%]
tests/unit/mcp_agent/llm/test_structured.py::test_structured_with_bad_json PASSED [ 57%]
tests/unit/mcp_agent/llm/test_structured.py::test_chat_turn_counting PASSED     [ 58%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_get_text PASSED   [ 58%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_get_image_data PASSED [ 59%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_get_resource_uri PASSED [ 59%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_is_text_content PASSED [ 60%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_is_image_content PASSED [ 60%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_is_resource_content PASSED [ 61%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_message_content_with_prompt_message PASSED [ 61%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_message_content_with_multipart PASSED [ 62%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_helpers.py::test_text_at_first_position PASSED [ 62%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptContent::test_apply_substitutions_content PASSED [ 63%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptContent::test_apply_substitutions_missing_var PASSED [ 63%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptContent::test_apply_substitutions_with_resources PASSED [ 64%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplate::test_simple_mode PASSED [ 64%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplate::test_delimited_mode PASSED [ 65%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplate::test_custom_delimiters PASSED [ 65%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplate::test_resources_in_template PASSED [ 66%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplate::test_multiple_resources_in_template PASSED [ 66%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplate::test_apply_substitutions PASSED [ 67%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplateLoader::test_load_from_file PASSED [ 67%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplateLoader::test_get_metadata_simple PASSED [ 68%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplateLoader::test_get_metadata_delimited PASSED [ 68%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestPromptTemplateLoader::test_load_template_with_resources PASSED [ 69%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestImageHandling::test_is_image_mime_type PASSED [ 69%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestImageHandling::test_create_image_content PASSED [ 70%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestImageHandling::test_binary_resource_handling PASSED [ 70%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestImageHandling::test_prompt_template_with_image PASSED [ 71%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestImageHandling::test_create_messages_with_image PASSED [ 71%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestImageHandling::test_resource_handling_functions PASSED [ 72%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestIntegration::test_simple_prompt_substitution PASSED [ 72%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestIntegration::test_delimited_prompt_substitution PASSED [ 73%]
tests/unit/mcp_agent/mcp/prompts/test_prompt_template.py::TestIntegration::test_resource_prompt_substitution PASSED [ 73%]
tests/unit/mcp_agent/mcp/prompts/test_template_multipart_integration.py::TestTemplateMultipartIntegration::test_template_to_multipart_conversion PASSED [ 74%]
tests/unit/mcp_agent/mcp/prompts/test_template_multipart_integration.py::TestTemplateMultipartIntegration::test_template_with_substitutions_to_multipart PASSED [ 74%]
tests/unit/mcp_agent/mcp/prompts/test_template_multipart_integration.py::TestTemplateMultipartIntegration::test_multipart_to_template_conversion PASSED [ 75%]
tests/unit/mcp_agent/mcp/prompts/test_template_multipart_integration.py::TestTemplateMultipartIntegration::test_round_trip_conversion PASSED [ 75%]
tests/unit/mcp_agent/mcp/prompts/test_template_multipart_integration.py::TestTemplateMultipartIntegration::test_save_and_load_from_file PASSED [ 76%]
tests/unit/mcp_agent/mcp/prompts/test_template_multipart_integration.py::TestTemplateMultipartIntegration::test_template_loader_integration PASSED [ 76%]
tests/unit/mcp_agent/mcp/test_mime_utils.py::TestMimeUtils::test_guess_mime_type PASSED [ 77%]
tests/unit/mcp_agent/mcp/test_prompt_format_utils.py::TestPromptFormatUtils::test_multipart_with_resources_to_delimited PASSED [ 77%]
tests/unit/mcp_agent/mcp/test_prompt_format_utils.py::TestPromptFormatUtils::test_delimited_with_resources_to_multipart PASSED [ 78%]
tests/unit/mcp_agent/mcp/test_prompt_format_utils.py::TestPromptFormatUtils::test_multiple_resources_in_one_message PASSED [ 78%]
tests/unit/mcp_agent/mcp/test_prompt_format_utils.py::TestPromptFormatUtils::test_image_handling PASSED [ 79%]
tests/unit/mcp_agent/mcp/test_prompt_format_utils.py::TestPromptFormatUtils::test_save_and_load_with_resources PASSED [ 79%]
tests/unit/mcp_agent/mcp/test_prompt_format_utils.py::TestPromptFormatUtils::test_round_trip_with_mime_types PASSED [ 80%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_from_prompt_messages_with_single_role PASSED [ 80%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_from_prompt_messages_with_multiple_roles PASSED [ 81%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_from_prompt_messages_with_mixed_content_types PASSED [ 81%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_to_prompt_messages PASSED [ 82%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_parse_get_prompt_result PASSED [ 82%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_empty_messages PASSED [ 83%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_round_trip_conversion PASSED [ 83%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_from_get_prompt_result PASSED [ 84%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_getting_last_text_empty PASSED [ 84%]
tests/unit/mcp_agent/mcp/test_prompt_message_multipart.py::TestPromptMessageMultipart::test_convenience_add_text PASSED [ 85%]
tests/unit/mcp_agent/mcp/test_prompt_multipart.py::test_create_messages_with_resources_alternating_roles PASSED [ 85%]
tests/unit/mcp_agent/mcp/test_prompt_multipart.py::test_create_messages_with_resources_roles_with_resources PASSED [ 86%]
tests/unit/mcp_agent/mcp/test_prompt_multipart.py::test_load_prompt_from_file PASSED [ 86%]
tests/unit/mcp_agent/mcp/test_prompt_multipart_conversion.py::test_resource_message_role_merging PASSED [ 87%]
tests/unit/mcp_agent/mcp/test_prompt_multipart_conversion.py::test_alternating_roles_with_no_resources PASSED [ 87%]
tests/unit/mcp_agent/mcp/test_prompt_multipart_conversion.py::test_playback_pattern_with_simple_messages PASSED [ 88%]
tests/unit/mcp_agent/mcp/test_prompt_render.py::TestPromptRender::test_render_text_only_message PASSED [ 88%]
tests/unit/mcp_agent/mcp/test_prompt_render.py::TestPromptRender::test_render_multiple_text_contents PASSED [ 89%]
tests/unit/mcp_agent/mcp/test_prompt_render.py::TestPromptRender::test_render_with_image_content PASSED [ 89%]
tests/unit/mcp_agent/mcp/test_prompt_render.py::TestPromptRender::test_render_with_embedded_text_resource PASSED [ 90%]
tests/unit/mcp_agent/mcp/test_prompt_render.py::TestPromptRender::test_render_with_long_embedded_text_resource PASSED [ 90%]
tests/unit/mcp_agent/mcp/test_prompt_render.py::TestPromptRender::test_render_with_blob_resource PASSED [ 91%]
tests/unit/mcp_agent/mcp/test_prompt_serialization.py::TestPromptSerialization::test_json_serialization_and_deserialization PASSED [ 91%]
tests/unit/mcp_agent/mcp/test_prompt_serialization.py::TestPromptSerialization::test_multipart_to_delimited_format PASSED [ 92%]
tests/unit/mcp_agent/mcp/test_prompt_serialization.py::TestPromptSerialization::test_multipart_with_resources_to_delimited_format PASSED [ 92%]
tests/unit/mcp_agent/mcp/test_prompt_serialization.py::TestPromptSerialization::test_multi_role_messages_to_delimited_format PASSED [ 93%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_absolute_path PASSED [ 93%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_already_valid_uri PASSED [ 94%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_empty_string PASSED [ 94%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_file_uri PASSED [ 95%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_normalize_uri PASSED [ 95%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_relative_path PASSED [ 96%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_simple_filename PASSED [ 96%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_uri_extraction_edge_cases PASSED [ 97%]
tests/unit/mcp_agent/mcp/test_resource_utils.py::TestUriNormalization::test_windows_path PASSED [ 97%]
tests/unit/mcp_agent/mcp/test_sampling.py::test_build_sampling_agent_config_with_system_prompt PASSED [ 98%]
tests/unit/mcp_agent/mcp/test_sampling.py::test_build_sampling_agent_config_default PASSED [ 98%]
tests/unit/mcp_agent/mcp/test_sampling.py::test_build_sampling_agent_config_empty_system_prompt PASSED [ 99%]
tests/unit/mcp_agent/mcp_agent/test_event_progress.py::test_event_conversion SKIPPED [100%]

================================== warnings summary ===================================
tests/unit/mcp_agent/llm/test_prepare_arguments.py:12
  /Users/monotykamary/VCS/working-remote/open-source/fast-agent/tests/unit/mcp_agent/llm/test_prepare_arguments.py:12: PytestCollectionWarning: cannot collect test class 'TestLLM' because it has a __init__ constructor (from: tests/unit/mcp_agent/llm/test_prepare_arguments.py)
    class TestLLM(AugmentedLLM):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
====================== 198 passed, 1 skipped, 1 warning in 1.36s ======================
================================= test session starts =================================
platform darwin -- Python 3.12.7, pytest-8.3.5, pluggy-1.5.0 -- /Users/monotykamary/VCS/working-remote/open-source/fast-agent/.venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/monotykamary/VCS/working-remote/open-source/fast-agent
configfile: pyproject.toml
plugins: anyio-4.9.0, asyncio-0.26.0, cov-6.1.1
asyncio: mode=Mode.STRICT, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collected 66 items                                                                    

tests/integration/api/test_api.py::test_agent_api_with_simple_prompt PASSED     [  1%]
tests/integration/api/test_api.py::test_agent_api_with_prompt_messages PASSED   [  3%]
tests/integration/api/test_api.py::test_agent_api_with_basic_playback PASSED    [  4%]
tests/integration/api/test_api.py::test_agent_api_with_default_calls PASSED     [  6%]
tests/integration/api/test_api.py::test_mixed_message_types PASSED              [  7%]
tests/integration/api/test_cli_and_mcp_server.py::test_agent_message_cli PASSED [  9%]
tests/integration/api/test_cli_and_mcp_server.py::test_agent_message_prompt_file PASSED [ 10%]
tests/integration/api/test_cli_and_mcp_server.py::test_agent_message_cli_quiet_flag PASSED [ 12%]
tests/integration/api/test_cli_and_mcp_server.py::test_agent_server_option_stdio PASSED [ 13%]
tests/integration/api/test_cli_and_mcp_server.py::test_agent_server_option_stdio_and_prompt_history PASSED [ 15%]
tests/integration/api/test_cli_and_mcp_server.py::test_agent_server_option_sse PASSED [ 16%]
tests/integration/api/test_describe_a2a.py::test_get_agent_card_and_tools PASSED [ 18%]
tests/integration/api/test_logger_textio.py::test_logger_textio_fileno PASSED   [ 19%]
tests/integration/api/test_logger_textio.py::test_logger_textio_write PASSED    [ 21%]
tests/integration/api/test_logger_textio.py::test_logger_textio_real_process PASSED [ 22%]
tests/integration/api/test_logger_textio.py::test_get_stderr_handler PASSED     [ 24%]
tests/integration/api/test_prompt_commands.py::test_command_handling_for_prompts PASSED [ 25%]
tests/integration/api/test_prompt_listing.py::test_get_all_prompts_with_none_server PASSED [ 27%]
tests/integration/api/test_prompt_listing.py::test_apply_prompt_with_namespaced_name PASSED [ 28%]
tests/integration/api/test_provider_keys.py::test_error_for_bad_provider_or_not_set PASSED [ 30%]
tests/integration/api/test_provider_keys.py::test_reads_keys_and_prioritises_config_file PASSED [ 31%]
tests/integration/api/test_provider_keys.py::test_ollama_generic_api_key PASSED [ 33%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_no_delimiters PASSED [ 34%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_no_delimiters_with_variables PASSED [ 36%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_multiturn PASSED [ 37%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_multiturn_with_subsitition PASSED [ 39%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_agent_interface_returns_prompts_list PASSED [ 40%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_get_prompt_with_server_param PASSED [ 42%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_apply_prompt_with_server_param PASSED [ 43%]
tests/integration/prompt-server/test_prompt_server_integration.py::test_handling_multipart_json_format PASSED [ 45%]
tests/integration/prompt-state/test_load_prompt_templates.py::test_load_simple_conversation_from_file PASSED [ 46%]
tests/integration/prompt-state/test_load_prompt_templates.py::test_load_conversation_with_attachments PASSED [ 48%]
tests/integration/prompt-state/test_load_prompt_templates.py::test_save_state_to_simple_text_file PASSED [ 50%]
tests/integration/prompt-state/test_load_prompt_templates.py::test_save_state_to_mcp_json_format PASSED [ 51%]
tests/integration/prompt-state/test_load_prompt_templates.py::test_round_trip_json_attachments PASSED [ 53%]
tests/integration/resources/test_resource_api.py::test_get_resource_with_explicit_server PASSED [ 54%]
tests/integration/resources/test_resource_api.py::test_get_resource_with_auto_server PASSED [ 56%]
tests/integration/resources/test_resource_api.py::test_list_resources PASSED    [ 57%]
tests/integration/resources/test_resource_api.py::test_error_handling PASSED    [ 59%]
tests/integration/resources/test_resource_api.py::test_with_resource_api PASSED [ 60%]
tests/integration/roots/test_roots.py::test_roots_returned PASSED               [ 62%]
tests/integration/sampling/test_sampling_integration.py::test_sampling_feature PASSED [ 63%]
tests/integration/sampling/test_sampling_integration.py::test_sampling_config PASSED [ 65%]
tests/integration/sampling/test_sampling_integration.py::test_sampling_passback PASSED [ 66%]
tests/integration/sampling/test_sampling_integration.py::test_sampling_multi_message_passback PASSED [ 68%]
tests/integration/workflow/chain/test_chain.py::test_disallows_empty_sequence PASSED [ 69%]
tests/integration/workflow/chain/test_chain.py::test_simple_chain PASSED        [ 71%]
tests/integration/workflow/chain/test_chain.py::test_cumulative_chain PASSED    [ 72%]
tests/integration/workflow/chain/test_chain.py::test_chain_functionality PASSED [ 74%]
tests/integration/workflow/chain/test_chain_passthrough.py::test_chain_passthrough PASSED [ 75%]
tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py::test_single_refinement_cycle PASSED [ 77%]
tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py::test_max_refinements_limit PASSED [ 78%]
tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py::test_early_stop_on_quality PASSED [ 80%]
tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py::test_structured_output PASSED [ 81%]
tests/integration/workflow/mixed/test_mixed_workflow.py::test_chaining_routers PASSED [ 83%]
tests/integration/workflow/mixed/test_mixed_workflow.py::test_router_selects_parallel PASSED [ 84%]
tests/integration/workflow/mixed/test_mixed_workflow.py::test_chain_in_eval_optimizer PASSED [ 86%]
tests/integration/workflow/orchestrator/test_orchestrator.py::test_full_plan_execution PASSED [ 87%]
tests/integration/workflow/orchestrator/test_orchestrator.py::test_iterative_plan_execution PASSED [ 89%]
tests/integration/workflow/orchestrator/test_orchestrator.py::test_invalid_agent_handling PASSED [ 90%]
tests/integration/workflow/orchestrator/test_orchestrator.py::test_max_iterations_handling PASSED [ 92%]
tests/integration/workflow/parallel/test_parallel_agent.py::test_parallel_run PASSED [ 93%]
tests/integration/workflow/parallel/test_parallel_agent.py::test_parallel_default_fan_in PASSED [ 95%]
tests/integration/workflow/router/test_router_agent.py::test_router_functionality PASSED [ 96%]
tests/integration/workflow/router/test_router_agent.py::test_router_structured_output PASSED [ 98%]
tests/integration/workflow/router/test_router_agent.py::test_router_invalid_agent_selection PASSED [100%]

================================== warnings summary ===================================
tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py:24
  /Users/monotykamary/VCS/working-remote/open-source/fast-agent/tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py:24: PytestCollectionWarning: cannot collect test class 'TestOutput' because it has a __init__ constructor (from: tests/integration/workflow/evaluator_optimizer/test_evaluator_optimizer.py)
    class TestOutput(BaseModel):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== 66 passed, 1 warning in 59.02s ============================

@monotykamary monotykamary marked this pull request as ready for review April 30, 2025 09:15
@monotykamary
Copy link
Author

monotykamary commented Apr 30, 2025

@evalstate I might have botched any semblance of code quality in this PR. Hopefully by moving the Google provider to google.genai, we can do more intricate tricks and support context caching on this side as well.

@evalstate
Copy link
Owner

THANKYOU!!!! I can't wait to play with this - it looks amazing. I'm going to get the other PRs and a couple of defect fixes released in the next day or 2 and then concentrate on bringing this in. I must confess, I did think implementing this would be quite good fun when you got to the different modalities - how has it been so far?

@monotykamary
Copy link
Author

I must confess, I did think implementing this would be quite good fun when you got to the different modalities - how has it been so far?

Oh don't get your hopes too high; this was an hour and a half work coded with fast-agent (more of super simple coding agent PoC I'm trying to make) and with Cline. The rest is mostly conceptual divide and conquer on Google's admittedly weird schema.

I am pretty confident we've got all of the major features working. The rest will most likely be edge-cases that isn't easily covered by the tests.

@rkunnamp
Copy link

rkunnamp commented May 4, 2025

Sorry for adding a comment not related to the thread here. But couldn't help ask @monotykamary if you could share the coding agent referred here - "super simple coding agent PoC I'm trying to make" . Quite curios to know how fastagent can be leveraged to make coding agents, that perhaps complements other tools out there including "Cline" that you have mentioned.

@evalstate
Copy link
Owner

OK - I've had a chance to take a quick look at this, and seems to be working pretty well.
I've added an alias for gemini2 to make it easier to test with :)

Having a quick look (and the results of the e2e smoke tests), structured outputs don't seem to be returned:
image

There's also something odd happening with Tool Calls (I've not had a chance to investigate), but if you run the chaining.py --model=gemini2 workflow example you get this error:

  File "/home/ssmith/source/fast-agent/src/mcp_agent/llm/providers/augmented_llm_google.py", line 152, in _google_completion
    available_tools = self._converter.convert_to_google_tools(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ssmith/source/fast-agent/src/mcp_agent/llm/providers/google_converter.py", line 96, in convert_to_google_tools
    parameters=types.Schema(
               ^^^^^^^^^^^^^
  File "/home/ssmith/source/fast-agent/.venv/lib/python3.12/site-packages/pydantic/main.py", line 253, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Schema
properties.max_length.exclusiveMaximum
  Extra inputs are not permitted [type=extra_forbidden, input_value=1000000, input_type=int]
    For further information visit https://errors.pydantic.dev/2.11/v/extra_forbidden
properties.max_length.exclusiveMinimum
  Extra inputs are not permitted [type=extra_forbidden, input_value=0, input_type=int]
    For further information visit https://errors.pydantic.dev/2.11/v/extra_forbidden

So not sure what's happening with that?! There are a few of the multimodal tests failing, but I suspect this issue above is the root cause.

To run the e2e tests (I've added gemini2 to a few of them), for the moment feel free to just comment out the other models where you don't have API keys/access etc.

Below is also some code if you just want an agent that uses structured outputs:

import asyncio
from typing import Annotated, List

from pydantic import BaseModel, Field

from mcp_agent.core.fastagent import FastAgent
from mcp_agent.core.prompt import Prompt
from mcp_agent.core.request_params import RequestParams

# Create the application
fast = FastAgent("fast-agent example")


class FormattedResponse(BaseModel):
    thinking: Annotated[
        str, Field(description="Your reflection on the conversation that is not seen by the user.")
    ]
    message: str


class CityInfo(BaseModel):
    name: str
    country: str
    population: int
    landmarks: List[str]


# Define the agent
@fast.agent(
    name="chat",
    instruction="You are a helpful AI Agent",
#    request_params=(RequestParams(maxTokens=8192)),
)
async def main():
    # use the --model command line switch or agent arguments to change model
    async with fast.run() as agent:
        thinking, response = await agent.chat.structured(
            [Prompt.user("Tell me about London.")],
            CityInfo,
        )


if __name__ == "__main__":
    asyncio.run(main())

Anyway, thank you for the contrib so far - just ping me on here/discord if you need any help/assistance with this :)

@monotykamary
Copy link
Author

Oh I'll have a quick look 🚀

@monotykamary monotykamary force-pushed the feat/implement-google-genai-provider branch from fca0262 to 4aa8dd1 Compare May 4, 2025 11:56
@monotykamary
Copy link
Author

Sorry for adding a comment not related to the thread here. But couldn't help ask @monotykamary if you could share the coding agent referred here - "super simple coding agent PoC I'm trying to make" . Quite curios to know how fastagent can be leveraged to make coding agents, that perhaps complements other tools out there including "Cline" that you have mentioned.

@rkunnamp An exercise up to the reader...

I'm kidding, it's really abusing the DesktopCommander MCP and formatting the agents in a way that pass history to each other. One example would be using an infinite loop and pass history whenever /STOP command is passed through the CLI. This would mimic role changes at the system prompt level:

import asyncio
from mcp_agent import RequestParams
from mcp_agent.core.fastagent import FastAgent

...

async def main():
    async with fast.run() as agent:
        while True:
            # Start an interactive session with architect
            await agent.interactive(agent="architect")
            # Pass architect's history to coder for generation
            await agent.coder.generate(agent.architect.message_history)
            # Start an interactive session with coder
            await agent.interactive(agent="coder")
            # Pass architect's history to coder for generation
            await agent.architect.generate(agent.coder.message_history)

if __name__ == "__main__":
    asyncio.run(main())

There are much better patterns that you can use to inject it directly into a following user prompt, instead of updating the system prompt like this to avoid:

  • Killing your prompt cache
  • Role drift due to essentially creating a "few shot" alignment scenario
  • Incorporate flows to do compression or problem distillation before switching agents

Really, the key player here is the MCP server and the agent architecture is up to you. skydeckai-code, or originally called mcp-server-aidd, was probably the first ever of its kind holding all of the tools for MCP-driven AI development.

systemPrompt=self.instruction,
parallel_tool_calls=False,
systemPrompt=self.instruction, # System instruction will be mapped in _google_completion
parallel_tool_calls=True, # Assume parallel tool calls are supported by default with native API
max_iterations=10,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated default

@evalstate
Copy link
Owner

evalstate commented May 4, 2025

That's great - looks like structured generation is really solid now. And these models are fast through this native interface - really cool :)

I've done another pass of testing and:

  • I've added an alias for gemini2.5; gemini2-flash is not actually great at tool calling... and was causing some issues in my testing. gemini 2.5 seems to fix those.
  • I think PDF should be supported for these models; so it may just be a mime type/conversion thing: https://ai.google.dev/gemini-api/docs/document-processing. I've also been working with @SecretiveShell who is pulling together a supported mime types database we might be able to use....
  • I think I got to the bottom of the ImageContent conversion (I've added a test_multipart_converter_google.py and borrowed a couple of tests from the OpenAI class). I'm not sure of the exact format of "Parts" required for this to work properly though? I made a small change to the handling of text/image content but not sure if it's correct for that API?

Anyway, great stuff so far - think this is a huge enhancement to fast-agent - thanks!

@monotykamary monotykamary force-pushed the feat/implement-google-genai-provider branch from a9c2b21 to 6907b1c Compare May 8, 2025 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Google Gemini Support
3 participants