- Update start command to check if the current directory is a Yao app root or a subdirectory, providing clearer error messages for users. - Modify installation logic to handle empty directories more effectively, ensuring the init app is installed only when appropriate. - Improve welcome message formatting and update links for documentation and community resources. Co-authored-by: Cursor <cursoragent@cursor.com>
6.8 KiB
6.8 KiB
Anthropic Provider Implementation Proposal
Overview
This proposal outlines the implementation plan for native Anthropic Claude API support in Yao Agent. Currently, all LLM connectors use type: "openai", and Anthropic detection relies on URL pattern matching, which is unreliable and architecturally incorrect.
Current Architecture
gou/connector/
├── openai/ # type: "openai" - handles all OpenAI-compatible APIs
├── moapi/ # type: "moapi"
├── redis/ # type: "redis"
└── ...
yao/agent/llm/
├── providers/
│ ├── factory.go # SelectProvider() - selects provider based on connector type
│ ├── base/ # Base provider implementation
│ └── openai/ # OpenAI-compatible provider
Current Flow:
- All LLM connectors declare
"type": "openai" factory.gousesconn.Is(connector.OPENAI)→ always true for LLMsDetectAPIFormat()guesses API format by URL patterns (unreliable)
Problem Statement
- No type distinction: Cannot differentiate Anthropic from OpenAI at connector level
- URL-based detection is fragile: Relies on hardcoded patterns like
"anthropic.com" - API incompatibility: Anthropic API uses different:
- Endpoint:
/messagesvs/chat/completions - Auth header:
x-api-keyvsBearertoken - Request format:
systemas separate field,max_tokensrequired - Response format: Different structure
- Endpoint:
Proposed Solution
Phase 1: gou/connector - Add Anthropic Connector Type
New files:
gou/connector/
├── anthropic/
│ ├── anthropic.go # Connector implementation
│ ├── types.go # Options, Capabilities structs
│ └── defaults.go # Default model capabilities
connector/types.go changes:
const (
// ... existing types
ANTHROPIC = 7 // New connector type
)
connector/anthropic/anthropic.go:
package anthropic
type Connector struct {
id string
file string
Name string `json:"name"`
Options Options `json:"options"`
}
type Options struct {
Host string `json:"host,omitempty"` // Default: https://api.anthropic.com
Model string `json:"model,omitempty"` // e.g., claude-sonnet-4-5
Key string `json:"key"` // API key
Version string `json:"version,omitempty"` // API version, default: 2024-01-01
Capabilities *Capabilities `json:"capabilities,omitempty"`
}
type Capabilities struct {
Vision interface{} `json:"vision,omitempty"`
ToolCalls bool `json:"tool_calls,omitempty"`
Streaming bool `json:"streaming,omitempty"`
// ... same as openai.Capabilities
}
DSL Example:
{
"label": "Claude Sonnet 4.5",
"type": "anthropic",
"options": {
"model": "claude-sonnet-4-5",
"key": "$ENV.ANTHROPIC_API_KEY",
"capabilities": {
"vision": "claude",
"tool_calls": true,
"streaming": true
}
}
}
Phase 2: yao/agent/llm - Add Anthropic Provider
New files:
yao/agent/llm/providers/
├── anthropic/
│ ├── anthropic.go # Provider implementation
│ └── types.go # Request/Response types
anthropic/anthropic.go:
package anthropic
type Provider struct {
*base.Provider
adapters []adapters.CapabilityAdapter
}
func New(conn connector.Connector, capabilities *Capabilities) *Provider
func (p *Provider) Stream(ctx, messages, options, handler) (*CompletionResponse, error)
func (p *Provider) Post(ctx, messages, options) (*CompletionResponse, error)
// Internal methods
func (p *Provider) buildRequestBody(messages, options, streaming) (map[string]interface{}, error)
func (p *Provider) convertMessages(messages []context.Message) []map[string]interface{}
Key Implementation Details:
- Message Conversion (OpenAI format → Anthropic format):
// OpenAI format:
// {"role": "system", "content": "..."}
// {"role": "user", "content": "..."}
// Anthropic format:
// system: "..." (separate field)
// messages: [{"role": "user", "content": "..."}]
- Request Building:
body := map[string]interface{}{
"model": model,
"max_tokens": maxTokens, // Required in Anthropic
"messages": convertedMessages,
}
if systemPrompt != "" {
body["system"] = systemPrompt
}
- HTTP Headers:
req.SetHeader("Content-Type", "application/json")
req.SetHeader("x-api-key", apiKey) // Not Bearer token
req.SetHeader("anthropic-version", "2024-01-01")
- SSE Parsing (different from OpenAI):
// Anthropic SSE events:
// event: message_start
// event: content_block_start
// event: content_block_delta
// event: content_block_stop
// event: message_delta
// event: message_stop
factory.go changes:
func SelectProvider(conn connector.Connector, options *CompletionOptions) (LLM, error) {
// ...
// Check connector type directly
if conn.Is(connector.ANTHROPIC) {
return anthropic.New(conn, options.Capabilities), nil
}
if conn.Is(connector.OPENAI) {
return openai.New(conn, options.Capabilities), nil
}
// Default fallback
return openai.New(conn, options.Capabilities), nil
}
Implementation Effort
| Component | Files | Lines (est.) | Effort |
|---|---|---|---|
| gou/connector/anthropic | 3 | ~250 | 2-3 hours |
| yao/agent/llm/providers/anthropic | 2 | ~600 | 4-6 hours |
| Tests | 4 | ~400 | 2-3 hours |
| Total | 9 | ~1250 | 8-12 hours |
Migration Path
- Backward Compatible: Existing
type: "openai"connectors continue to work - New Connectors: Use
type: "anthropic"for direct Anthropic API access - Proxy Services: OpenRouter, AWS Bedrock still use
type: "openai"(they provide OpenAI-compatible endpoints)
Testing Strategy
- Unit Tests: Message conversion, request building
- Integration Tests: Real API calls (with test API key)
- Connector Tests: gou connector parsing and validation
Alternative Considered
URL-based detection in yao layer only (current approach):
- Pros: No gou changes needed
- Cons: Fragile, architecturally incorrect, no connector-level validation
Conclusion: Rejected. Proper connector type is the cleaner solution.
References
Next Steps
- Review and approve this proposal
- Implement gou/connector/anthropic (Phase 1)
- Implement yao/agent/llm/providers/anthropic (Phase 2)
- Update yao-init connectors to use
type: "anthropic" - Write tests and documentation