Skip to content
Open
2 changes: 1 addition & 1 deletion .agents/skills/switchyard-codebase-exploration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Before making a code change, be able to name:

- the client ingress path: CLI, FastAPI endpoint, Python API, or route-table host
- the chain slot touched: request-side component, `LLMBackend`, response-side component, `TranslationEngine`, or wiring only
- the wire formats involved: OpenAI Chat, Anthropic Messages, OpenAI Responses, streaming/non-streaming
- the wire formats involved: OpenAI Chat, Anthropic Messages, OpenAI Responses, Gemini generateContent, streaming/non-streaming
- the profile/route-table/export path that exposes the behavior
- the focused tests and the broad validation gate to run

Expand Down
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,13 @@ switchyard/
│ │ ├── base.py # ChatResponse, ChatResponseType compatibility re-export
│ │ ├── openai_chat.py # ResponseStream
│ │ ├── openai_responses.py # ResponsesApiStream
│ │ └── anthropic.py # AnthropicResponseStream
│ │ ├── anthropic.py # AnthropicResponseStream
│ │ └── gemini.py # GeminiResponseStream
│ ├── backends/ # LLMBackend implementations
│ │ ├── openai_llm_backend.py # OpenAiPassthroughBackend
│ │ ├── openai_native_backend.py # OpenAiNativeBackend
│ │ ├── anthropic_native_llm_backend.py # AnthropicNativeBackend
│ │ ├── gemini_native_llm_backend.py # GeminiNativeBackend
│ │ ├── latency_service_llm_backend.py # LatencyServiceLLMBackend
│ │ ├── llm_target.py # LlmTarget, BackendFormat
│ │ ├── multi_llm_backend.py # MultiLlmBackend helpers
Expand All @@ -187,6 +189,7 @@ switchyard/
│ ├── endpoints/ # FastAPI endpoint wrappers (require `nemo-switchyard[server]`)
│ │ ├── openai_chat_endpoint.py # OpenAIChatEndpoint
│ │ ├── anthropic_messages_endpoint.py # AnthropicMessagesEndpoint
│ │ ├── gemini_endpoint.py # GeminiEndpoint
│ │ ├── responses_endpoint.py # ResponsesEndpoint
│ │ ├── stats_endpoint.py # StatsEndpoint
│ │ ├── sse_helpers.py
Expand Down Expand Up @@ -330,6 +333,7 @@ uvicorn.run(build_switchyard_app(switchyard), port=4000)
| `OPENAI_API_KEY` | API key for OpenAI-compatible backends |
| `OPENAI_BASE_URL` | Base URL for OpenAI-compatible API |
| `ANTHROPIC_API_KEY` | API key for Anthropic Claude |
| `GEMINI_API_KEY` | API key for Google Gemini generateContent backends |
| `NVIDIA_API_KEY` | API key for NVIDIA NIM / Inference Hub |
| `OPENROUTER_API_KEY` | OpenRouter key for examples and saved provider setup; pass via `--api-key` or `switchyard configure` |
| `SWITCHYARD_INTAKE_CAPTURE_CONTENT` | Set truthy to include prompt/response text in intake; default off (metadata-only) |
Expand Down
12 changes: 11 additions & 1 deletion crates/switchyard-components-v2/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
use std::sync::Arc;

use async_trait::async_trait;
use switchyard_components::backends::{AnthropicNativeBackend, OpenAiNativeBackend};
use switchyard_components::backends::{
AnthropicNativeBackend, GeminiNativeBackend, OpenAiNativeBackend,
};
use switchyard_core::{
BackendFormat, ChatRequest, ChatResponse, LlmTarget, Result, SwitchyardError,
};
Expand Down Expand Up @@ -49,6 +51,7 @@ pub(crate) fn native_target_backend(target: LlmTarget) -> Result<TargetBackend>
Arc::new(OpenAiNativeBackend::new(target.clone())?)
}
BackendFormat::Anthropic => Arc::new(AnthropicNativeBackend::new(target.clone())?),
BackendFormat::Gemini => Arc::new(GeminiNativeBackend::new(target.clone())?),
BackendFormat::Auto => {
return Err(SwitchyardError::InvalidConfig(format!(
"target {} must have a resolved backend format",
Expand All @@ -72,3 +75,10 @@ impl ProfileBackend for AnthropicNativeBackend {
self.call_without_context(request).await
}
}

#[async_trait]
impl ProfileBackend for GeminiNativeBackend {
async fn call(&self, request: &ChatRequest) -> Result<ChatResponse> {
self.call_without_context(request).await
}
}
13 changes: 7 additions & 6 deletions crates/switchyard-components/src/backends/anthropic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,13 @@ impl AnthropicTransport for ReqwestAnthropicTransport {
fn validate_target_format(target: &LlmTarget) -> Result<()> {
match target.format {
BackendFormat::Anthropic => Ok(()),
BackendFormat::Auto | BackendFormat::OpenAi | BackendFormat::Responses => {
Err(SwitchyardError::InvalidConfig(format!(
"AnthropicNativeBackend requires a target with resolved Anthropic format, got {:?} for {}",
target.format, target.id
)))
}
BackendFormat::Auto
| BackendFormat::OpenAi
| BackendFormat::Responses
| BackendFormat::Gemini => Err(SwitchyardError::InvalidConfig(format!(
"AnthropicNativeBackend requires a target with resolved Anthropic format, got {:?} for {}",
target.format, target.id
))),
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/switchyard-components/src/backends/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub(crate) fn request_wire_format(request_type: ChatRequestType) -> WireFormat {
ChatRequestType::OpenAiChat => WireFormat::OpenAiChat,
ChatRequestType::OpenAiResponses => WireFormat::OpenAiResponses,
ChatRequestType::Anthropic => WireFormat::AnthropicMessages,
ChatRequestType::Gemini => WireFormat::GeminiGenerateContent,
}
}

Expand Down
Loading