mirror of
https://github.com/we-promise/sure.git
synced 2026-05-30 07:49:01 +00:00
Introduces Provider::Anthropic alongside Provider::Openai, implementing the LlmConcept chat_response contract over the official anthropic Ruby SDK. Batch ops, PDF, and RAG land in follow-up PRs. - Provider::Anthropic uses Messages API for sync and streaming responses - ChatConfig builds requests with ephemeral prompt-cache markers on the system prompt and the last tool definition - MessageFormatter reconstructs multi-turn history (text + tool_use + tool_result blocks) from raw Message records, including the paired user-role tool_result turn Anthropic requires after every tool_use - ChatParser maps Anthropic Message into the shared ChatResponse Data - Registry, Setting, User, Chat default model wired for ANTHROPIC_* envs and Setting.anthropic_*; LLM_PROVIDER selects between providers - Responder forwards raw conversation_history (Array<Message>) so providers without hosted conversation state can rebuild context - OpenAI provider accepts and ignores the new kwarg (no behavior change) Tests cover provider init, model gating, MessageFormatter for all turn shapes, ChatConfig request building (max_tokens, system cache, tool conversion), ChatParser for text / tool_use / mixed blocks, Registry discovery, and mocked chat_response success / error / function_request paths. Live VCR cassettes recorded in a follow-up with a real key. Stacked PRs: 2/5 batch ops + cost ledger, 3/5 PDF, 4/5 pgvector RAG, 5/5 settings UI + disclosure.
111 lines
3.5 KiB
Ruby
111 lines
3.5 KiB
Ruby
require "test_helper"
|
|
|
|
class Provider::RegistryTest < ActiveSupport::TestCase
|
|
test "providers filters out nil values when provider is not configured" do
|
|
# Ensure no LLM provider is configured
|
|
ClimateControl.modify(
|
|
"OPENAI_ACCESS_TOKEN" => nil,
|
|
"ANTHROPIC_ACCESS_TOKEN" => nil,
|
|
"ANTHROPIC_API_KEY" => nil
|
|
) do
|
|
Setting.stubs(:openai_access_token).returns(nil)
|
|
Setting.stubs(:anthropic_access_token).returns(nil)
|
|
|
|
registry = Provider::Registry.for_concept(:llm)
|
|
|
|
# Should return empty array instead of [nil]
|
|
assert_equal [], registry.providers
|
|
end
|
|
end
|
|
|
|
test "providers returns configured providers" do
|
|
# Mock a configured OpenAI provider
|
|
mock_provider = mock("openai_provider")
|
|
Provider::Registry.stubs(:openai).returns(mock_provider)
|
|
|
|
registry = Provider::Registry.for_concept(:llm)
|
|
|
|
assert_equal [ mock_provider ], registry.providers
|
|
end
|
|
|
|
test "get_provider raises error when provider not found for concept" do
|
|
registry = Provider::Registry.for_concept(:llm)
|
|
|
|
error = assert_raises(Provider::Registry::Error) do
|
|
registry.get_provider(:nonexistent)
|
|
end
|
|
|
|
assert_match(/Provider 'nonexistent' not found for concept: llm/, error.message)
|
|
end
|
|
|
|
test "get_provider returns nil when provider not configured" do
|
|
# Ensure OpenAI is not configured
|
|
ClimateControl.modify("OPENAI_ACCESS_TOKEN" => nil) do
|
|
Setting.stubs(:openai_access_token).returns(nil)
|
|
|
|
registry = Provider::Registry.for_concept(:llm)
|
|
|
|
# Should return nil when provider method exists but returns nil
|
|
assert_nil registry.get_provider(:openai)
|
|
end
|
|
end
|
|
|
|
test "anthropic provider returns nil when no credentials are configured" do
|
|
ClimateControl.modify(
|
|
"ANTHROPIC_ACCESS_TOKEN" => nil,
|
|
"ANTHROPIC_API_KEY" => nil
|
|
) do
|
|
Setting.stubs(:anthropic_access_token).returns(nil)
|
|
|
|
assert_nil Provider::Registry.get_provider(:anthropic)
|
|
end
|
|
end
|
|
|
|
test "anthropic provider initializes from ANTHROPIC_API_KEY env" do
|
|
ClimateControl.modify("ANTHROPIC_API_KEY" => "sk-ant-test", "ANTHROPIC_ACCESS_TOKEN" => nil) do
|
|
Setting.stubs(:anthropic_access_token).returns(nil)
|
|
|
|
provider = Provider::Registry.get_provider(:anthropic)
|
|
|
|
assert_instance_of Provider::Anthropic, provider
|
|
end
|
|
end
|
|
|
|
test "anthropic provider falls back to Setting when ENV is empty" do
|
|
ClimateControl.modify(
|
|
"ANTHROPIC_ACCESS_TOKEN" => "",
|
|
"ANTHROPIC_API_KEY" => "",
|
|
"ANTHROPIC_BASE_URL" => "",
|
|
"ANTHROPIC_MODEL" => ""
|
|
) do
|
|
Setting.stubs(:anthropic_access_token).returns("sk-ant-from-setting")
|
|
Setting.stubs(:anthropic_base_url).returns(nil)
|
|
Setting.stubs(:anthropic_model).returns(nil)
|
|
|
|
provider = Provider::Registry.get_provider(:anthropic)
|
|
|
|
assert_instance_of Provider::Anthropic, provider
|
|
end
|
|
end
|
|
|
|
test "openai provider falls back to Setting when ENV is empty string" do
|
|
# Mock ENV to return empty string (common in Docker/env files)
|
|
# Use stub_env helper which properly stubs ENV access
|
|
ClimateControl.modify(
|
|
"OPENAI_ACCESS_TOKEN" => "",
|
|
"OPENAI_URI_BASE" => "",
|
|
"OPENAI_MODEL" => ""
|
|
) do
|
|
Setting.stubs(:openai_access_token).returns("test-token-from-setting")
|
|
Setting.stubs(:openai_uri_base).returns(nil)
|
|
Setting.stubs(:openai_model).returns(nil)
|
|
|
|
provider = Provider::Registry.get_provider(:openai)
|
|
|
|
# Should successfully create provider using Setting value
|
|
assert_not_nil provider
|
|
assert_instance_of Provider::Openai, provider
|
|
end
|
|
end
|
|
end
|