Skip to content

Conversation

vrushankportkey
Copy link
Collaborator

@vrushankportkey vrushankportkey commented Aug 27, 2025

PR: Google ADK Integration for Portkey Python SDK (optional)

Motivation

  • Customers want a thin, first-party adapter to plug Portkey into Google ADK’s BaseLlm interface without changing their app code.
  • Mirrors the existing Strands adapter pattern so teams can standardize on Portkey while using ADK’s agent ecosystem.

Summary

  • Adds a new PortkeyAdk adapter that translates ADK requests to Portkey’s OpenAI-compatible API and converts streamed/final responses back into ADK LlmResponse.
  • Gated behind an optional extra adk so normal installs aren’t impacted.
  • Includes tests, examples, and README updates with copy-pasteable snippets.

What’s included

  • New adapter

    • portkey_ai/integrations/adk.py
    • Features:
      • Configurable system role: system_role="developer" | "system" (default "developer").
      • Tools/function calling: when tools are present, defaults to tool_choice="auto" unless the caller overrides it.
      • Streaming and non-streaming support: emits partial chunks during streaming and aggregates a final message at the end.
      • Response format passthrough: best-effort ADK schema to OpenAI JSON schema conversion.
      • Optional dependency gating with helpful ImportError copy:
        • pip install 'portkey-ai[adk]'
      • Typing notes:
        • Adds targeted # type: ignore[arg-type] and # type: ignore[union-attr] on ADK function-call parts where ADK/GenAI typing is looser than runtime behavior.
  • Packaging

    • setup.cfg: new optional extra adk:
      • google-adk
      • google-genai
  • Documentation

    • README.md:
      • New “Google ADK Integration (optional)” section with install and usage.
      • Streaming example updated to print only partial chunks (checks resp.partial) to avoid duplicate final output.
      • Non-streaming example added.
  • Example script

    • examples/hello_world_portkey_adk.py
      • Non-streaming example that prints a single final response.
      • Env:
        • PORTKEY_API_KEY (required)
        • MODEL_SLUG (optional; defaults to @openai/gpt-4o-mini)
  • Tests

    • tests/integrations/test_adk_adapter.py
      • Covers:
        • Non-streaming path returns a single final message.
        • Streaming path emits partial chunks and a final aggregated response.
      • Skips cleanly if google-adk isn’t installed (pytest.importorskip("google.adk")).
  • Minor improvement

    • portkey_ai/integrations/strands.py: await and cast the streaming creation result to satisfy mypy.
  • Lint/Type config

    • pyproject.toml: Ruff ignores E501 (line-length) and sets line-length=88 to match Black.

Usage

  • Install

    pip install 'portkey-ai[adk]'
  • Streaming (prints only partial chunks to avoid duplicates)

    import asyncio
    from google.adk.models.llm_request import LlmRequest
    from google.genai import types
    from portkey_ai.integrations.adk import PortkeyAdk
    
    llm = PortkeyAdk(
        api_key="PORTKEY_API_KEY",
        model="@openai/gpt-4o-mini",
    )
    
    req = LlmRequest(
        model="@openai/gpt-4o-mini",
        contents=[
            types.Content(
                role="user",
                parts=[types.Part.from_text(text="Tell me a short programming joke.")],
            )
        ],
    )
    
    async def main():
        async for resp in llm.generate_content_async(req, stream=True):
            if getattr(resp, "partial", False) and resp.content and resp.content.parts:
                for p in resp.content.parts:
                    if getattr(p, "text", None):
                        print(p.text, end="")
        print()
    
    asyncio.run(main())
  • Non‑streaming (single final response)

    import asyncio
    from google.adk.models.llm_request import LlmRequest
    from google.genai import types
    from portkey_ai.integrations.adk import PortkeyAdk
    
    llm = PortkeyAdk(
        api_key="PORTKEY_API_KEY",
        model="@openai/gpt-4o-mini",
    )
    
    req = LlmRequest(
        model="@openai/gpt-4o-mini",
        contents=[
            types.Content(
                role="user",
                parts=[types.Part.from_text(text="Give me a one-line programming joke (final only).")],
            )
        ],
    )
    
    async def main():
        final_text = []
        async for resp in llm.generate_content_async(req, stream=False):
            if resp.content and resp.content.parts:
                for p in resp.content.parts:
                    if getattr(p, "text", None):
                        final_text.append(p.text)
        print("".join(final_text))
    
    asyncio.run(main())
  • Options

    • system_role: default "developer", can be set to "system" for providers that expect strict system role semantics.
    • Tools: when tools are present in the ADK request, tool_choice="auto" is set by default unless explicitly provided.

Local verification

  • Virtual environment and dev tooling

    python3 -m venv .venv
    . .venv/bin/activate
    pip install -U pip
    pip install -e '.[dev,adk]'
  • Lint/format/type

    ruff check --fix .
    black .
    mypy portkey_ai
  • Run tests

    pytest -q tests/integrations/test_adk_adapter.py
  • Run example

    export PORTKEY_API_KEY='YOUR_PORTKEY_API_KEY'
    # optional: export MODEL_SLUG='@openai/gpt-4o-mini'
    python examples/hello_world_portkey_adk.py

Backwards compatibility

  • No breaking changes. The ADK integration is opt-in via the adk extra and imports only when used.

Known limitations and notes

  • ADK/GenAI types can evolve; where runtime behavior differs from static types, we added targeted # type: ignore[...] to keep mypy green without weakening overall typing.
  • Response schema conversion is best-effort.

Checklist

  • New adapter: portkey_ai/integrations/adk.py
  • Optional extra: adk (google-adk, google-genai)
  • README: install, streaming and non-streaming examples
  • Example script under examples/
  • Tests for streaming and non‑streaming (skip if ADK not installed)
  • Lint/format/type clean (ruff/black/mypy)
  • No breaking changes

Release notes

  • Added optional Google ADK integration. Install with pip install 'portkey-ai[adk]'. Supports streaming, tools, and a configurable system role. Includes examples and tests.

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. auto:enhancement labels Aug 27, 2025
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Sep 19, 2025
@vrushankportkey vrushankportkey changed the title Add Strands Integration Add Strands & Google ADK Integration Sep 19, 2025
@csgulati09
Copy link
Collaborator

Failing and getting Type Error while running the boilerplate README code

Traceback (most recent call last):
  File ".../adktest.py", line 17, in <module>
    parts=[types.Part.from_text("Tell me a short programming joke.")],
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Part.from_text() takes 1 positional argument but 2 were given

@dosubot dosubot bot added the lgtm label Sep 22, 2025
@csgulati09 csgulati09 merged commit 6bbf4d4 into main Sep 22, 2025
5 of 6 checks passed
@csgulati09 csgulati09 deleted the strands-integrations branch September 22, 2025 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto:enhancement lgtm size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants