ask-llm-providers 0.2.2 → 0.3.0
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 +4 -4
- data/CHANGELOG.md +18 -1
- data/lib/ask/llm/aliases.json +11 -0
- data/lib/ask/llm/aliases.rb +62 -0
- data/lib/ask/llm/catalog.rb +194 -0
- data/lib/ask/llm/models/anthropic.json +188 -0
- data/lib/ask/llm/models/bedrock.json +1 -0
- data/lib/ask/llm/models/cloudflare.json +1 -0
- data/lib/ask/llm/models/deepseek.json +85 -0
- data/lib/ask/llm/models/gemini.json +151 -0
- data/lib/ask/llm/models/mimo.json +22 -0
- data/lib/ask/llm/models/mistral.json +62 -0
- data/lib/ask/llm/models/ollama.json +136 -0
- data/lib/ask/llm/models/openai.json +294 -0
- data/lib/ask/llm/models/opencode.json +189 -0
- data/lib/ask/llm/models/opencode_go.json +149 -0
- data/lib/ask/llm/models/openrouter.json +115 -0
- data/lib/ask/llm/models_schema.json +74 -0
- data/lib/ask/llm/version.rb +1 -1
- data/lib/ask-llm-providers.rb +4 -17
- metadata +17 -2
- data/lib/ask/llm/models/openai.rb +0 -69
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 925a11549bdce89470327b583421ae71aa3b8aa4385efacb9baead5ee8aa7f27
|
|
4
|
+
data.tar.gz: 8ec764b1b392cf83ac70afb56eb78df2e118150f2e25951580dd1efac628a8af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4bff20df39628b886645cb48d8eb812d6f5e32ae8f5de2ffc433bd577c7e5dd4bf64b429b0efeffefbee79f2ce7af3bdc48a5cabb60f63e0ea0612e61d352130
|
|
7
|
+
data.tar.gz: 93bef4f89008fbcefcee9362ef9ca35369230012777e9202683d781ebdf872901d796fb847f2cbd74cf86faaa17801233bcaa2731f6058ee72ef45f6d7dadc2c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
## [0.
|
|
1
|
+
## [0.3.0] — 2026-07-14
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- **Model catalog system** — `Ask::LLM::Catalog` loads model definitions from per-provider JSON files (`lib/ask/llm/models/*.json`), user overrides (`~/.ask-llm-providers/models.json`), and provider API `list_models()` on explicit refresh.
|
|
5
|
+
- **Per-provider model JSONs** — 12 JSON files (openai, anthropic, gemini, deepseek, opencode, opencode_go, mimo, openrouter, ollama, mistral, bedrock, cloudflare) with id, name, provider, capabilities, context window, modalities, and pricing.
|
|
6
|
+
- **Model aliases** — `Ask::LLM::Aliases` resolves short names (e.g. `claude-sonnet-4` → `claude-sonnet-4-6`). Alias entries are automatically registered into `Ask::ModelCatalog` so `ModelCatalog.find` works with alias names.
|
|
7
|
+
- **User config support** — `~/.ask-llm-providers/models.json` overrides bundled model fields or adds custom models.
|
|
8
|
+
- **`opencode.json` includes `deepseek-v4-flash`** — matches the default model configuration.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Removed hardcoded `Ask::LLM::Models::OPENAI_MODELS` constants — replaced with catalog-driven model loading.
|
|
12
|
+
- `Ask::LLM::Aliases.resolve` now aliases `deepseek-v4` → `deepseek-v4-flash`, `gpt-4o-latest` → `gpt-4o`, `gpt-4.1-latest` → `gpt-4.1`.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Model entries now include `"provider"` field in JSON files (was missing from generated data).
|
|
16
|
+
- User config merges properly override bundled values (was keeping old values on conflict).
|
|
17
|
+
|
|
18
|
+
## [0.2.2] — 2026-06-25
|
|
2
19
|
|
|
3
20
|
### Changed
|
|
4
21
|
- Extended per-provider tests (Anthropic 18t, Google 14t, DeepSeek 16t, Mistral, Ollama, Cloudflare, Bedrock). Fixed providers_test.rb syntax error. RuboCop, overcommit, gemspec test, SimpleCov, CI.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"claude-sonnet-4": "claude-sonnet-4-6",
|
|
3
|
+
"claude-sonnet-4-5": "claude-sonnet-4-6",
|
|
4
|
+
"claude-opus-4": "claude-opus-4-7",
|
|
5
|
+
"claude-opus-4-5": "claude-opus-4-7",
|
|
6
|
+
"claude-haiku-4": "claude-haiku-4-5",
|
|
7
|
+
"gemini-3": "gemini-3-flash",
|
|
8
|
+
"deepseek-v4": "deepseek-v4-flash",
|
|
9
|
+
"gpt-4o-latest": "gpt-4o",
|
|
10
|
+
"gpt-4.1-latest": "gpt-4.1"
|
|
11
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ask
|
|
6
|
+
module LLM
|
|
7
|
+
# Resolves model name aliases to canonical model IDs.
|
|
8
|
+
#
|
|
9
|
+
# Aliases are defined in aliases.json and allow users to refer
|
|
10
|
+
# to models by shorter or more familiar names. Resolution is
|
|
11
|
+
# provider-scoped — you can alias "claude-sonnet-4" to different
|
|
12
|
+
# canonical IDs depending on which provider serves it.
|
|
13
|
+
#
|
|
14
|
+
# Ask::LLM::Aliases.resolve("claude-sonnet-4")
|
|
15
|
+
# # => "claude-sonnet-4-6"
|
|
16
|
+
#
|
|
17
|
+
# Aliases are loaded lazily from the bundled JSON file.
|
|
18
|
+
module Aliases
|
|
19
|
+
ALIASES_PATH = File.expand_path("aliases.json", __dir__)
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# Resolve an alias to a canonical model ID.
|
|
23
|
+
# Returns the input name unchanged if no alias is registered.
|
|
24
|
+
def resolve(name)
|
|
25
|
+
load_aliases unless @aliases
|
|
26
|
+
@aliases[name.to_s] || name.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Register a custom alias at runtime.
|
|
30
|
+
def register(short_name, canonical_id)
|
|
31
|
+
load_aliases unless @aliases
|
|
32
|
+
@aliases[short_name.to_s] = canonical_id.to_s
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Reload aliases from the bundled JSON file.
|
|
36
|
+
def reload!
|
|
37
|
+
@aliases = nil
|
|
38
|
+
load_aliases
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# All registered aliases (for introspection).
|
|
42
|
+
def all
|
|
43
|
+
load_aliases unless @aliases
|
|
44
|
+
@aliases.dup
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def load_aliases
|
|
50
|
+
@aliases = {}
|
|
51
|
+
path = ALIASES_PATH
|
|
52
|
+
return unless File.exist?(path)
|
|
53
|
+
|
|
54
|
+
raw = JSON.parse(File.read(path))
|
|
55
|
+
raw.each { |k, v| @aliases[k.to_s] = v.to_s }
|
|
56
|
+
rescue JSON::ParserError
|
|
57
|
+
# Invalid aliases file — log and use empty map
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Ask
|
|
7
|
+
module LLM
|
|
8
|
+
# Orchestrates model catalog loading from multiple sources:
|
|
9
|
+
#
|
|
10
|
+
# 1. Bundled JSON files in lib/ask/llm/models/*.json (shipped with the gem)
|
|
11
|
+
# 2. ~/.ask-llm-providers/models.json (user-defined overrides)
|
|
12
|
+
# 3. Provider API list_models() calls (on explicit refresh!)
|
|
13
|
+
#
|
|
14
|
+
# Loaded models are registered into Ask::ModelCatalog for use
|
|
15
|
+
# by ask-agent, ask-mcp, and llm-proxy.
|
|
16
|
+
#
|
|
17
|
+
# Ask::LLM::Catalog.load! # load bundled + user config
|
|
18
|
+
# Ask::LLM::Catalog.refresh! # also fetch from provider APIs
|
|
19
|
+
#
|
|
20
|
+
class Catalog
|
|
21
|
+
class Error < StandardError; end
|
|
22
|
+
class LoadError < Error; end
|
|
23
|
+
|
|
24
|
+
USER_CONFIG_PATH = File.expand_path("~/.ask-llm-providers/models.json").freeze
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# Load bundled model definitions and user overrides into Ask::ModelCatalog.
|
|
28
|
+
# Idempotent — subsequent calls clear and reload.
|
|
29
|
+
def load!
|
|
30
|
+
instance.clear
|
|
31
|
+
instance.load_bundled
|
|
32
|
+
instance.load_user_config
|
|
33
|
+
instance.register_all
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Like load! but also fetches model lists from configured providers'
|
|
38
|
+
# list_models() APIs. Unknown models are added with minimal metadata.
|
|
39
|
+
def refresh!
|
|
40
|
+
load!
|
|
41
|
+
instance.fetch_from_providers
|
|
42
|
+
instance.register_all
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def symbolize_keys(hash)
|
|
49
|
+
hash.transform_keys { |k| k.respond_to?(:to_sym) ? k.to_sym : k }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def instance
|
|
53
|
+
@instance ||= new
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def initialize
|
|
58
|
+
@entries = []
|
|
59
|
+
@model_keys = Set.new
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def clear
|
|
63
|
+
@entries.clear
|
|
64
|
+
@model_keys.clear
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Load bundled model JSONs from the gem's lib/ask/llm/models/ directory.
|
|
68
|
+
def load_bundled
|
|
69
|
+
pattern = File.expand_path("models/*.json", __dir__)
|
|
70
|
+
Dir[pattern].sort.each do |path|
|
|
71
|
+
raw = JSON.parse(File.read(path))
|
|
72
|
+
raw.each { |entry| add_entry(entry) }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Load user-defined model overrides from ~/.ask-llm-providers/models.json.
|
|
77
|
+
# Silently skipped if the file doesn't exist.
|
|
78
|
+
def load_user_config
|
|
79
|
+
path = USER_CONFIG_PATH
|
|
80
|
+
return unless File.exist?(path)
|
|
81
|
+
|
|
82
|
+
raw = JSON.parse(File.read(path))
|
|
83
|
+
unless raw.is_a?(Array)
|
|
84
|
+
warn "Warning: #{path} should be a JSON array of model entries, got #{raw.class}"
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
raw.each { |entry| merge_or_add(entry) }
|
|
89
|
+
rescue JSON::ParserError => e
|
|
90
|
+
warn "Warning: Failed to parse #{path}: #{e.message}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Fetch model lists from all configured providers via their list_models() API.
|
|
94
|
+
# Adds unknown models with minimal metadata (no capability guessing).
|
|
95
|
+
def fetch_from_providers
|
|
96
|
+
Ask::Provider.providers.each do |slug, provider_class|
|
|
97
|
+
next unless provider_class.configured?(nil)
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
provider = provider_class.new
|
|
101
|
+
models = provider.list_models
|
|
102
|
+
models.each do |m|
|
|
103
|
+
add_entry(m) unless @model_keys.include?([m[:id], slug.to_s])
|
|
104
|
+
end
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
warn "Warning: Failed to fetch models from #{slug}: #{e.message}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Register all accumulated entries into Ask::ModelCatalog.
|
|
112
|
+
# Also registers alias entries so models can be found by alias name.
|
|
113
|
+
def register_all
|
|
114
|
+
@entries.each do |entry|
|
|
115
|
+
info = build_model_info(entry)
|
|
116
|
+
Ask::ModelCatalog.instance.register(info)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
register_alias_entries
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
# For each alias (short_name → canonical_id), register a duplicate
|
|
125
|
+
# ModelInfo for every canonical entry whose id matches.
|
|
126
|
+
def register_alias_entries
|
|
127
|
+
Ask::LLM::Aliases.all.each do |short_name, canonical_id|
|
|
128
|
+
next if short_name == canonical_id
|
|
129
|
+
|
|
130
|
+
@entries.each do |entry|
|
|
131
|
+
next unless entry["id"] == canonical_id || entry[:id] == canonical_id
|
|
132
|
+
|
|
133
|
+
alias_entry = entry.merge("id" => short_name)
|
|
134
|
+
info = build_model_info(alias_entry)
|
|
135
|
+
Ask::ModelCatalog.instance.register(info)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def symbolize_keys(hash)
|
|
141
|
+
hash.transform_keys { |k| k.respond_to?(:to_sym) ? k.to_sym : k }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def add_entry(entry)
|
|
145
|
+
key = entry_key(entry)
|
|
146
|
+
return if @model_keys.include?(key)
|
|
147
|
+
|
|
148
|
+
@entries << entry
|
|
149
|
+
@model_keys << key
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def merge_or_add(entry)
|
|
153
|
+
key = entry_key(entry)
|
|
154
|
+
existing = @entries.find { |e| entry_key(e) == key }
|
|
155
|
+
|
|
156
|
+
if existing
|
|
157
|
+
existing.merge!(entry)
|
|
158
|
+
else
|
|
159
|
+
@entries << entry
|
|
160
|
+
@model_keys << key
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def entry_key(entry)
|
|
165
|
+
id = entry["id"] || entry[:id]
|
|
166
|
+
provider = entry["provider"] || entry[:provider]
|
|
167
|
+
[id, provider.to_s]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def build_model_info(entry)
|
|
171
|
+
e = entry.transform_keys(&:to_sym)
|
|
172
|
+
modalities = e[:modalities]
|
|
173
|
+
modalities = symbolize_keys(modalities) if modalities
|
|
174
|
+
|
|
175
|
+
Ask::ModelInfo.new(
|
|
176
|
+
id: e[:id],
|
|
177
|
+
name: e[:name] || e[:id],
|
|
178
|
+
provider: e[:provider],
|
|
179
|
+
family: e[:family],
|
|
180
|
+
capabilities: Array(e[:capabilities]),
|
|
181
|
+
context_window: e[:context_window],
|
|
182
|
+
max_output_tokens: e[:max_output_tokens],
|
|
183
|
+
modalities: modalities || { input: %w[text], output: %w[text] },
|
|
184
|
+
pricing: e[:pricing] || {},
|
|
185
|
+
knowledge_cutoff: e[:knowledge_cutoff] ? Date.parse(e[:knowledge_cutoff].to_s) : nil,
|
|
186
|
+
created_at: e[:created_at] ? Date.parse(e[:created_at].to_s) : nil,
|
|
187
|
+
metadata: (e[:metadata] || {}).merge(source: e[:metadata]&.dig("source") || "bundled")
|
|
188
|
+
)
|
|
189
|
+
rescue Date::Error
|
|
190
|
+
Ask::ModelInfo.new(id: e[:id], provider: e[:provider])
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "claude-sonnet-4-5",
|
|
4
|
+
"name": "Claude Sonnet 4.5",
|
|
5
|
+
"family": "claude_sonnet",
|
|
6
|
+
"context_window": 200000,
|
|
7
|
+
"max_output_tokens": 64000,
|
|
8
|
+
"capabilities": [
|
|
9
|
+
"function_calling",
|
|
10
|
+
"streaming",
|
|
11
|
+
"structured_output",
|
|
12
|
+
"vision",
|
|
13
|
+
"reasoning",
|
|
14
|
+
"prompt_caching",
|
|
15
|
+
"tool_choice",
|
|
16
|
+
"parallel_tool_calls"
|
|
17
|
+
],
|
|
18
|
+
"modalities": {
|
|
19
|
+
"input": [
|
|
20
|
+
"text",
|
|
21
|
+
"image",
|
|
22
|
+
"pdf"
|
|
23
|
+
],
|
|
24
|
+
"output": [
|
|
25
|
+
"text"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"provider": "anthropic"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "claude-sonnet-4",
|
|
32
|
+
"name": "Claude Sonnet 4",
|
|
33
|
+
"family": "claude_sonnet",
|
|
34
|
+
"context_window": 200000,
|
|
35
|
+
"max_output_tokens": 64000,
|
|
36
|
+
"capabilities": [
|
|
37
|
+
"function_calling",
|
|
38
|
+
"streaming",
|
|
39
|
+
"structured_output",
|
|
40
|
+
"vision",
|
|
41
|
+
"reasoning",
|
|
42
|
+
"prompt_caching",
|
|
43
|
+
"tool_choice",
|
|
44
|
+
"parallel_tool_calls"
|
|
45
|
+
],
|
|
46
|
+
"modalities": {
|
|
47
|
+
"input": [
|
|
48
|
+
"text",
|
|
49
|
+
"image",
|
|
50
|
+
"pdf"
|
|
51
|
+
],
|
|
52
|
+
"output": [
|
|
53
|
+
"text"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"provider": "anthropic"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "claude-4-opus",
|
|
60
|
+
"name": "Claude Opus 4",
|
|
61
|
+
"family": "claude_opus",
|
|
62
|
+
"context_window": 200000,
|
|
63
|
+
"max_output_tokens": 64000,
|
|
64
|
+
"capabilities": [
|
|
65
|
+
"function_calling",
|
|
66
|
+
"streaming",
|
|
67
|
+
"structured_output",
|
|
68
|
+
"vision",
|
|
69
|
+
"reasoning",
|
|
70
|
+
"prompt_caching",
|
|
71
|
+
"tool_choice",
|
|
72
|
+
"parallel_tool_calls"
|
|
73
|
+
],
|
|
74
|
+
"modalities": {
|
|
75
|
+
"input": [
|
|
76
|
+
"text",
|
|
77
|
+
"image",
|
|
78
|
+
"pdf"
|
|
79
|
+
],
|
|
80
|
+
"output": [
|
|
81
|
+
"text"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"provider": "anthropic"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "claude-haiku-4-5",
|
|
88
|
+
"name": "Claude Haiku 4.5",
|
|
89
|
+
"family": "claude_haiku",
|
|
90
|
+
"context_window": 200000,
|
|
91
|
+
"max_output_tokens": 64000,
|
|
92
|
+
"capabilities": [
|
|
93
|
+
"function_calling",
|
|
94
|
+
"streaming",
|
|
95
|
+
"structured_output",
|
|
96
|
+
"vision",
|
|
97
|
+
"reasoning",
|
|
98
|
+
"prompt_caching",
|
|
99
|
+
"tool_choice",
|
|
100
|
+
"parallel_tool_calls"
|
|
101
|
+
],
|
|
102
|
+
"modalities": {
|
|
103
|
+
"input": [
|
|
104
|
+
"text",
|
|
105
|
+
"image",
|
|
106
|
+
"pdf"
|
|
107
|
+
],
|
|
108
|
+
"output": [
|
|
109
|
+
"text"
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"provider": "anthropic"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "claude-opus-4-5",
|
|
116
|
+
"name": "Claude Opus 4.5",
|
|
117
|
+
"family": "claude_opus",
|
|
118
|
+
"context_window": 200000,
|
|
119
|
+
"max_output_tokens": 64000,
|
|
120
|
+
"capabilities": [
|
|
121
|
+
"function_calling",
|
|
122
|
+
"streaming",
|
|
123
|
+
"structured_output",
|
|
124
|
+
"vision",
|
|
125
|
+
"reasoning",
|
|
126
|
+
"prompt_caching",
|
|
127
|
+
"tool_choice",
|
|
128
|
+
"parallel_tool_calls"
|
|
129
|
+
],
|
|
130
|
+
"modalities": {
|
|
131
|
+
"input": [
|
|
132
|
+
"text",
|
|
133
|
+
"image",
|
|
134
|
+
"pdf"
|
|
135
|
+
],
|
|
136
|
+
"output": [
|
|
137
|
+
"text"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
"provider": "anthropic"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "claude-3.5-sonnet",
|
|
144
|
+
"name": "Claude 3.5 Sonnet",
|
|
145
|
+
"family": "claude_sonnet",
|
|
146
|
+
"context_window": 200000,
|
|
147
|
+
"max_output_tokens": 8192,
|
|
148
|
+
"capabilities": [
|
|
149
|
+
"function_calling",
|
|
150
|
+
"streaming",
|
|
151
|
+
"vision",
|
|
152
|
+
"reasoning"
|
|
153
|
+
],
|
|
154
|
+
"modalities": {
|
|
155
|
+
"input": [
|
|
156
|
+
"text",
|
|
157
|
+
"image"
|
|
158
|
+
],
|
|
159
|
+
"output": [
|
|
160
|
+
"text"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
"provider": "anthropic"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "claude-3.5-haiku",
|
|
167
|
+
"name": "Claude 3.5 Haiku",
|
|
168
|
+
"family": "claude_haiku",
|
|
169
|
+
"context_window": 200000,
|
|
170
|
+
"max_output_tokens": 8192,
|
|
171
|
+
"capabilities": [
|
|
172
|
+
"function_calling",
|
|
173
|
+
"streaming",
|
|
174
|
+
"vision",
|
|
175
|
+
"reasoning"
|
|
176
|
+
],
|
|
177
|
+
"modalities": {
|
|
178
|
+
"input": [
|
|
179
|
+
"text",
|
|
180
|
+
"image"
|
|
181
|
+
],
|
|
182
|
+
"output": [
|
|
183
|
+
"text"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"provider": "anthropic"
|
|
187
|
+
}
|
|
188
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "deepseek-v4-flash",
|
|
4
|
+
"name": "DeepSeek V4 Flash",
|
|
5
|
+
"family": "deepseek_v4",
|
|
6
|
+
"context_window": 1000000,
|
|
7
|
+
"max_output_tokens": 384000,
|
|
8
|
+
"capabilities": [
|
|
9
|
+
"function_calling",
|
|
10
|
+
"streaming",
|
|
11
|
+
"reasoning"
|
|
12
|
+
],
|
|
13
|
+
"modalities": {
|
|
14
|
+
"input": [
|
|
15
|
+
"text"
|
|
16
|
+
],
|
|
17
|
+
"output": [
|
|
18
|
+
"text"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"provider": "deepseek"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "deepseek-v4-pro",
|
|
25
|
+
"name": "DeepSeek V4 Pro",
|
|
26
|
+
"family": "deepseek_v4",
|
|
27
|
+
"context_window": 1000000,
|
|
28
|
+
"max_output_tokens": 384000,
|
|
29
|
+
"capabilities": [
|
|
30
|
+
"function_calling",
|
|
31
|
+
"streaming",
|
|
32
|
+
"reasoning"
|
|
33
|
+
],
|
|
34
|
+
"modalities": {
|
|
35
|
+
"input": [
|
|
36
|
+
"text"
|
|
37
|
+
],
|
|
38
|
+
"output": [
|
|
39
|
+
"text"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"provider": "deepseek"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "deepseek-chat",
|
|
46
|
+
"name": "DeepSeek Chat",
|
|
47
|
+
"family": "deepseek_v3",
|
|
48
|
+
"context_window": 64000,
|
|
49
|
+
"max_output_tokens": 8192,
|
|
50
|
+
"capabilities": [
|
|
51
|
+
"function_calling",
|
|
52
|
+
"streaming"
|
|
53
|
+
],
|
|
54
|
+
"modalities": {
|
|
55
|
+
"input": [
|
|
56
|
+
"text"
|
|
57
|
+
],
|
|
58
|
+
"output": [
|
|
59
|
+
"text"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"provider": "deepseek"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "deepseek-reasoner",
|
|
66
|
+
"name": "DeepSeek Reasoner",
|
|
67
|
+
"family": "deepseek_r1",
|
|
68
|
+
"context_window": 64000,
|
|
69
|
+
"max_output_tokens": 8192,
|
|
70
|
+
"capabilities": [
|
|
71
|
+
"function_calling",
|
|
72
|
+
"streaming",
|
|
73
|
+
"reasoning"
|
|
74
|
+
],
|
|
75
|
+
"modalities": {
|
|
76
|
+
"input": [
|
|
77
|
+
"text"
|
|
78
|
+
],
|
|
79
|
+
"output": [
|
|
80
|
+
"text"
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"provider": "deepseek"
|
|
84
|
+
}
|
|
85
|
+
]
|