ask-llm-providers 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 677ec905a0f11d7072c4574d03193b85720065778678e940b38252d2adc2f1a0
4
- data.tar.gz: 9cb65bb51e2ea18e6b7c1b92e0d7fcce64aab4b4d8e9d5493215200928e35eb9
3
+ metadata.gz: e1c7c2b703b28c45fa414ddf18bb9aa7ddf896c00d97418c01b8f55966e61b52
4
+ data.tar.gz: d0c7a219f49a0f981f2991fa72e249c813450a9d84f2269478ac733bfdad608a
5
5
  SHA512:
6
- metadata.gz: cf49fac238b8ce8a9a8df31dcab9a3854d35401eb45699bbb964b01bf50417e544ba5095a9c9b1e40c68b11095f1d3e35bad386b5549a6a6a8793e28ebb0b85b
7
- data.tar.gz: 99e31531be1bbc2b0930f957630118d77de620e3e668750f04ea966a1bcd0c627339623ba59fae69464c7aa07bfe39be354f5934f145d2b6dcb3d8fb24c73c81
6
+ metadata.gz: dd4dbd35bc0efe7d19a5844246c76e117601d3da4d90831eedc2e53a6345c048666b28af0d5c831dfe62557eb1434c95c51668fa010bfb676032a3159ba57195
7
+ data.tar.gz: 47be20c465f50f4cd586df677d5f984d14c034df110606c965085bcf2a17a05f19d45e4716eda4f41884abffb3afca622a39b6ed81961a27c4bea88754b258cf
data/README.md CHANGED
@@ -7,14 +7,14 @@ from `ask-core` with a capabilities-based interface.
7
7
 
8
8
  | Provider | Auth | Implementation |
9
9
  |---|---|---|
10
- | **OpenAI** + all OpenAI-compatible | `Ask::Auth.resolve(:openai_api_key)` | `Ask::Provider::OpenAI` |
11
- | **Anthropic** (Claude) | `Ask::Auth.resolve(:anthropic_api_key)` | `Ask::Provider::Anthropic` |
12
- | **Google Gemini** | `Ask::Auth.resolve(:gemini_api_key)` | `Ask::Provider::Google` |
13
- | **Vertex AI** | GCP service account | `Ask::Provider::VertexAI` |
14
- | **Amazon Bedrock** | AWS credentials chain | `Ask::Provider::Bedrock` |
15
- | **Ollama** (local) | None needed | `Ask::Provider::Ollama` |
16
- | **Mistral AI** | `Ask::Auth.resolve(:mistral_api_key)` | `Ask::Provider::Mistral` |
17
- | **Cloudflare Workers AI** | `Ask::Auth.resolve(:cloudflare_api_key)` | `Ask::Provider::Cloudflare` |
10
+ | **OpenAI** + all OpenAI-compatible | `Ask::Auth.resolve(:openai_api_key)` | `Ask::Providers::OpenAI` |
11
+ | **Anthropic** (Claude) | `Ask::Auth.resolve(:anthropic_api_key)` | `Ask::Providers::Anthropic` |
12
+ | **Google Gemini** | `Ask::Auth.resolve(:gemini_api_key)` | `Ask::Providers::Google` |
13
+ | **Vertex AI** | GCP service account | `Ask::Providers::Google` (via Vertex) |
14
+ | **Amazon Bedrock** | AWS credentials chain | `Ask::Providers::Bedrock` |
15
+ | **Ollama** (local) | None needed | `Ask::Providers::Ollama` |
16
+ | **Mistral AI** | `Ask::Auth.resolve(:mistral_api_key)` | `Ask::Providers::Mistral` |
17
+ | **Cloudflare Workers AI** | `Ask::Auth.resolve(:cloudflare_api_key)` | `Ask::Providers::Cloudflare` |
18
18
 
19
19
  ## Installation
20
20
 
@@ -32,7 +32,7 @@ models = Ask::Models.find("gpt-4o")
32
32
  # => { provider: :openai, capabilities: [...] }
33
33
 
34
34
  # Use a provider directly
35
- provider = Ask::Provider::OpenAI.new
35
+ provider = Ask::Providers::OpenAI.new
36
36
  provider.chat(conversation, tools: [], model: "gpt-4o") do |chunk|
37
37
  print chunk.content
38
38
  end
@@ -43,21 +43,75 @@ end
43
43
  Each provider and model exposes its capabilities:
44
44
 
45
45
  ```ruby
46
- provider = Ask::Provider::OpenAI.new
46
+ provider = Ask::Providers::OpenAI.new
47
47
  provider.capabilities
48
- # => [:chat, :streaming, :tool_calls, :vision, :thinking,
48
+ # => { chat: true, streaming: true, tool_calls: true, vision: true, thinking: true,
49
49
  # :structured_output, :embed, :transcribe, :paint, :moderate]
50
50
 
51
51
  model = Ask::Models.find("claude-sonnet-4-5")
52
52
  model[:capabilities]
53
- # => [:chat, :streaming, :tool_calls, :vision, :thinking, :prompt_caching]
53
+ # => { chat: true, streaming: true, tool_calls: true, vision: true, thinking: true, :prompt_caching]
54
54
 
55
55
  # Unsupported capabilities raise a helpful error
56
- provider = Ask::Provider::Anthropic.new
56
+ provider = Ask::Providers::Anthropic.new
57
57
  provider.embed(["text"], model: "claude-sonnet-4-5")
58
58
  # => Ask::CapabilityNotSupported: Anthropic (claude-sonnet-4-5) does not support embeddings.
59
59
  ```
60
60
 
61
+
62
+
63
+ ## Streaming
64
+
65
+ ```ruby
66
+ stream = provider.chat(
67
+ [{ role: "user", content: "Tell me a story" }],
68
+ model: "gpt-4o",
69
+ stream: true
70
+ ) do |chunk|
71
+ print chunk.content
72
+ end
73
+
74
+ # After streaming completes, you can access the full response
75
+ puts stream.accumulated_text
76
+ puts stream.accumulated_usage
77
+ ```
78
+
79
+ ## Tool Calls
80
+
81
+ ```ruby
82
+ tools = [{
83
+ name: "get_weather",
84
+ description: "Get weather for a location",
85
+ parameters: {
86
+ type: "object",
87
+ properties: { location: { type: "string" } },
88
+ required: ["location"]
89
+ }
90
+ }]
91
+
92
+ response = provider.chat(
93
+ [{ role: "user", content: "What's the weather in NYC?" }],
94
+ model: "gpt-4o",
95
+ tools: tools
96
+ )
97
+ # response.tool_call? => true
98
+ # response.tool_calls => [{ id: "call_1", name: "get_weather", arguments: '{"location":"NYC"}' }]
99
+ ```
100
+
101
+ ## Error Handling
102
+
103
+ Provider errors map to structured `Ask::Error` types:
104
+
105
+ ```ruby
106
+ Ask::RateLimitError # 429 — retry with backoff
107
+ Ask::Unauthorized # 401/403 — check your API key
108
+ Ask::ServerError # 500 — provider issue
109
+ Ask::ServiceUnavailable # 503 — temporary
110
+ Ask::ContextLengthExceeded # context window exceeded
111
+ Ask::ProviderError # other provider errors
112
+ Ask::CapabilityNotSupported # feature not available on this model
113
+ ```
114
+
61
115
  ## Development
62
116
 
63
117
  ```bash
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model definitions for OpenAI and compatible providers.
4
+ # Registered on gem load via Ask::Models.register.
5
+ module Ask
6
+ module LLM
7
+ module Models
8
+ OPENAI_MODELS = [
9
+ { id: "gpt-4o", family: "gpt4o", capabilities: %w[chat streaming function_calling structured_output vision], context: 128000, output: 16384 },
10
+ { id: "gpt-4o-mini", family: "gpt4o_mini", capabilities: %w[chat streaming function_calling structured_output vision], context: 128000, output: 16384 },
11
+ { id: "gpt-4.1", family: "gpt41", capabilities: %w[chat streaming function_calling structured_output vision], context: 1047576, output: 32768 },
12
+ { id: "gpt-4.1-mini", family: "gpt41_mini", capabilities: %w[chat streaming function_calling structured_output vision], context: 1047576, output: 32768 },
13
+ { id: "gpt-4.1-nano", family: "gpt41_nano", capabilities: %w[chat streaming function_calling structured_output vision], context: 1047576, output: 32768 },
14
+ { id: "gpt-4-turbo", family: "gpt4_turbo", capabilities: %w[chat streaming function_calling vision], context: 128000, output: 4096 },
15
+ { id: "gpt-4", family: "gpt4", capabilities: %w[chat streaming function_calling], context: 8192, output: 8192 },
16
+ { id: "o1", family: "o1", capabilities: %w[chat streaming function_calling structured_output reasoning], context: 200000, output: 100000 },
17
+ { id: "o1-mini", family: "o1_mini", capabilities: %w[chat streaming function_calling reasoning], context: 128000, output: 65536 },
18
+ { id: "o3-mini", family: "o3_mini", capabilities: %w[chat streaming function_calling structured_output reasoning], context: 200000, output: 100000 },
19
+ { id: "gpt-4o-audio-preview", family: "gpt4o_audio", capabilities: %w[chat streaming audio], context: 128000 },
20
+ { id: "gpt-4o-realtime-preview", family: "gpt4o_realtime", capabilities: %w[chat streaming audio], context: 128000 },
21
+ { id: "gpt-4o-mini-realtime-preview", family: "gpt4o_mini_realtime", capabilities: %w[chat streaming audio], context: 128000 },
22
+ { id: "gpt-4.5-preview", family: "gpt45", capabilities: %w[chat streaming function_calling structured_output vision], context: 128000, output: 16384 },
23
+ { id: "text-embedding-3-large", family: "embedding3_large", capabilities: %w[embed], context: 8191 },
24
+ { id: "text-embedding-3-small", family: "embedding3_small", capabilities: %w[embed], context: 8191 },
25
+ { id: "whisper-1", family: "whisper", capabilities: %w[transcribe] },
26
+ { id: "tts-1", family: "tts1", capabilities: %w[tts] },
27
+ { id: "tts-1-hd", family: "tts1_hd", capabilities: %w[tts] },
28
+ { id: "dall-e-3", family: "dall_e", capabilities: %w[paint] },
29
+ { id: "dall-e-2", family: "dall_e", capabilities: %w[paint] }
30
+ ].freeze
31
+
32
+ ANTHROPIC_MODELS = [
33
+ { id: "claude-sonnet-4-5", family: "claude_sonnet", capabilities: %w[chat streaming function_calling vision thinking prompt_caching], context: 200000, output: 8192 },
34
+ { id: "claude-sonnet-4", family: "claude_sonnet", capabilities: %w[chat streaming function_calling vision thinking prompt_caching], context: 200000, output: 8192 },
35
+ { id: "claude-4-opus", family: "claude_opus", capabilities: %w[chat streaming function_calling vision thinking prompt_caching], context: 200000, output: 8192 },
36
+ { id: "claude-3.5-sonnet", family: "claude_sonnet", capabilities: %w[chat streaming function_calling vision thinking], context: 200000, output: 8192 },
37
+ { id: "claude-3.5-haiku", family: "claude_haiku", capabilities: %w[chat streaming function_calling vision thinking], context: 200000, output: 8192 },
38
+ { id: "claude-3-opus", family: "claude_opus", capabilities: %w[chat streaming function_calling vision thinking], context: 200000, output: 4096 },
39
+ { id: "claude-3-sonnet", family: "claude_sonnet", capabilities: %w[chat streaming function_calling vision], context: 200000, output: 4096 },
40
+ { id: "claude-3-haiku", family: "claude_haiku", capabilities: %w[chat streaming function_calling vision], context: 200000, output: 4096 }
41
+ ].freeze
42
+
43
+ GOOGLE_MODELS = [
44
+ { id: "gemini-2.5-pro", family: "gemini", capabilities: %w[chat streaming function_calling structured_output vision reasoning], context: 1048576, output: 65536 },
45
+ { id: "gemini-2.5-flash", family: "gemini", capabilities: %w[chat streaming function_calling structured_output vision], context: 1048576, output: 65536 },
46
+ { id: "gemini-2.0-flash", family: "gemini", capabilities: %w[chat streaming function_calling structured_output vision], context: 1048576, output: 8192 },
47
+ { id: "gemini-1.5-pro", family: "gemini", capabilities: %w[chat streaming function_calling structured_output vision], context: 2097152, output: 8192 },
48
+ { id: "gemini-1.5-flash", family: "gemini", capabilities: %w[chat streaming function_calling structured_output vision], context: 1048576, output: 8192 },
49
+ { id: "text-embedding-004", family: "embedding", capabilities: %w[embed], context: 2048 }
50
+ ].freeze
51
+
52
+ MISTRAL_MODELS = [
53
+ { id: "mistral-large-2501", family: "mistral", capabilities: %w[chat streaming function_calling structured_output], context: 128000, output: 4096 },
54
+ { id: "mistral-small-2501", family: "mistral", capabilities: %w[chat streaming function_calling structured_output], context: 128000, output: 4096 },
55
+ { id: "mistral-embed", family: "mistral", capabilities: %w[embed], context: 8192 }
56
+ ].freeze
57
+
58
+ OLLAMA_MODELS = [
59
+ { id: "llama3.2", family: "llama", capabilities: %w[chat streaming], context: 8192 },
60
+ { id: "llama3.3", family: "llama", capabilities: %w[chat streaming], context: 8192 },
61
+ { id: "mistral", family: "mistral", capabilities: %w[chat streaming], context: 8192 },
62
+ { id: "gemma3", family: "gemma", capabilities: %w[chat streaming], context: 8192 },
63
+ { id: "phi4", family: "phi", capabilities: %w[chat streaming], context: 8192 },
64
+ { id: "qwen2.5", family: "qwen", capabilities: %w[chat streaming], context: 32768 },
65
+ { id: "deepseek-r1", family: "deepseek", capabilities: %w[chat streaming reasoning], context: 8192 }
66
+ ].freeze
67
+ end
68
+ end
69
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module LLM
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
@@ -33,7 +33,7 @@ module Ask
33
33
  end
34
34
 
35
35
  def embed(_texts, model: nil)
36
- raise Ask::UnsupportedFeature, "Anthropic does not support embeddings"
36
+ raise Ask::CapabilityNotSupported, "Anthropic does not support embeddings"
37
37
  end
38
38
 
39
39
  def list_models
@@ -25,7 +25,7 @@ module Ask
25
25
  end
26
26
 
27
27
  def embed(_texts, model: nil)
28
- raise Ask::UnsupportedFeature, "Bedrock does not support embeddings via Converse API"
28
+ raise Ask::CapabilityNotSupported, "Bedrock does not support embeddings via Converse API"
29
29
  end
30
30
 
31
31
  def list_models
@@ -51,13 +51,13 @@ module Ask
51
51
  end
52
52
 
53
53
  class << self
54
- def slug; "openai"; end
55
- def capabilities
54
+ def slug; "openai"; end
55
+ def capabilities
56
56
  { chat: true, streaming: true, tool_calls: true, vision: true, thinking: true, structured_output: true, embed: true, transcribe: true, paint: true, moderate: true }
57
57
  end
58
58
  def configuration_options; %i[api_key base_url organization_id project_id]; end
59
59
  def configuration_requirements; %i[api_key]; end
60
- def configured?(config)
60
+ def configured?(config)
61
61
  (config.respond_to?(:api_key) && !config.api_key.to_s.empty?) ||
62
62
  (config.respond_to?(:openai_api_key) && !config.openai_api_key.to_s.empty?)
63
63
  end
@@ -10,6 +10,7 @@ require "base64"
10
10
  # Common infrastructure
11
11
  require_relative "ask/llm/config"
12
12
  require_relative "ask/llm/http"
13
+ require_relative "ask/llm/models/openai"
13
14
 
14
15
  # Load providers
15
16
  require_relative "ask/provider/openai"
@@ -28,3 +29,21 @@ Ask::Provider.register(:bedrock, Ask::Providers::Bedrock)
28
29
  Ask::Provider.register(:ollama, Ask::Providers::Ollama)
29
30
  Ask::Provider.register(:mistral, Ask::Providers::Mistral)
30
31
  Ask::Provider.register(:cloudflare, Ask::Providers::Cloudflare)
32
+
33
+
34
+ # Register known models for each provider in the catalog
35
+ [
36
+ [Ask::Providers::OpenAI, Ask::LLM::Models::OPENAI_MODELS],
37
+ [Ask::Providers::Anthropic, Ask::LLM::Models::ANTHROPIC_MODELS],
38
+ [Ask::Providers::Google, Ask::LLM::Models::GOOGLE_MODELS],
39
+ [Ask::Providers::Mistral, Ask::LLM::Models::MISTRAL_MODELS],
40
+ [Ask::Providers::Ollama, Ask::LLM::Models::OLLAMA_MODELS]
41
+ ].each do |provider, models|
42
+ models.each do |m|
43
+ Ask::ModelCatalog.instance.register(Ask::ModelInfo.new(
44
+ id: m[:id], provider: provider.slug, family: m[:family],
45
+ capabilities: m[:capabilities],
46
+ context_window: m[:context], max_output_tokens: m[:output]
47
+ ))
48
+ end
49
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-llm-providers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto
@@ -13,16 +13,16 @@ dependencies:
13
13
  name: ask-core
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '0.1'
18
+ version: 0.1.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '0.1'
25
+ version: 0.1.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ask-auth
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -79,6 +79,20 @@ dependencies:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: base64
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.2'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.2'
82
96
  - !ruby/object:Gem::Dependency
83
97
  name: minitest
84
98
  requirement: !ruby/object:Gem::Requirement
@@ -163,6 +177,7 @@ files:
163
177
  - lib/ask-llm-providers.rb
164
178
  - lib/ask/llm/config.rb
165
179
  - lib/ask/llm/http.rb
180
+ - lib/ask/llm/models/openai.rb
166
181
  - lib/ask/llm/version.rb
167
182
  - lib/ask/provider/anthropic.rb
168
183
  - lib/ask/provider/bedrock.rb