Skip to content

feat(gemini): Phase 1 -- basic chat (generateContent) #1593

@bug-ops

Description

@bug-ops

Part of #1592

Scope

Implement the basic non-streaming chat provider for Google Gemini using the generateContent endpoint.

Files to Create

  • crates/zeph-llm/src/gemini.rs -- GeminiProvider struct, request/response types, LlmProvider impl

Files to Modify

  • crates/zeph-llm/src/lib.rs -- add pub mod gemini;
  • crates/zeph-llm/src/any.rs -- add Gemini(GeminiProvider) variant, delegate_provider, list_models_remote, set_status_tx, with_generation_overrides
  • crates/zeph-core/src/config/types.rs -- add Gemini to ProviderKind, GeminiConfig struct, gemini_api_key to ResolvedSecrets
  • crates/zeph-core/src/bootstrap/provider.rs -- add ProviderKind::Gemini arm in create_provider, "gemini" arm in create_named_provider
  • crates/zeph-core/src/instructions.rs -- add ProviderKind::Gemini to detection_paths match
  • src/init.rs -- add Gemini option to provider selection wizard
  • src/acp.rs -- add Gemini arm to ACP model switching

Key Implementation Details

  • API key via ?key= query param (NOT Authorization header)
  • System messages extracted into systemInstruction field (separate from contents)
  • Role mapping: User -> "user", Assistant -> "model"
  • Model name in URL path: /v1beta/models/{model}:generateContent
  • Parse candidates[0].content.parts[0].text from response
  • Track usageMetadata for last_usage() (promptTokenCount, candidatesTokenCount)
  • Context window lookup by model name (1M for 2.5-pro/flash, 2.0-flash)

Request/Response Types

GeminiRequest { contents, system_instruction, generation_config }
GeminiContent { role, parts }
GeminiPart { text?, inline_data?, function_call?, function_response? }
GeminiGenerationConfig { temperature, top_p, top_k, max_output_tokens }
GeminiResponse { candidates, usage_metadata }
GeminiCandidate { content, finish_reason }
GeminiUsageMetadata { prompt_token_count, candidates_token_count, total_token_count }

Acceptance Criteria

  • provider = "gemini" in config.toml works end-to-end
  • Non-streaming chat produces correct responses
  • API key resolved from vault (ZEPH_GEMINI_API_KEY)
  • Error handling: rate limit, invalid key, model not found
  • context_window() returns correct value per model
  • name() returns "gemini"
  • supports_streaming() returns false (Phase 2)
  • supports_tool_use() returns false (Phase 3)
  • supports_embeddings() returns false (Phase 5)
  • supports_vision() returns true
  • debug_request_json() mirrors actual API payload
  • Init wizard includes Gemini option
  • Unit tests with wiremock for success/error paths

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions