ollama-client 1.3.0 → 1.4.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/.env.example +11 -0
- data/API_CONTRACT.md +79 -8
- data/CHANGELOG.md +45 -0
- data/CONTRIBUTING.md +8 -0
- data/README.md +205 -273
- data/ROADMAP.md +23 -0
- data/docs/API_GAPS.md +18 -141
- data/docs/ARCHITECTURE.md +141 -0
- data/docs/AREAS_FOR_CONSIDERATION.md +13 -1
- data/docs/CLOUD.md +19 -0
- data/docs/CONSOLE_IMPROVEMENTS.md +20 -1
- data/docs/ECOSYSTEM_GEMS.md +41 -0
- data/docs/ECOSYSTEM_STRATEGY.md +151 -0
- data/docs/GETTING_STARTED.md +16 -8
- data/docs/INTEGRATION_TESTING.md +23 -5
- data/docs/PRODUCTION_FIXES.md +15 -3
- data/docs/QUICK_START.md +1 -1
- data/docs/README.md +1 -0
- data/docs/RUBYLLM_ADOPTION_MATRIX.md +273 -0
- data/docs/adr/001-openai-boundary.md +12 -0
- data/docs/adr/002-transport-abstraction.md +12 -0
- data/docs/adr/003-response-normalization.md +12 -0
- data/docs/adr/004-mock-transport.md +16 -0
- data/docs/adr/005-error-taxonomy.md +16 -0
- data/docs/adr/006-stream-runtime.md +16 -0
- data/docs/ecosystem/BOUNDARIES.md +19 -0
- data/docs/ecosystem/DEPENDENCY_GRAPH.md +14 -0
- data/docs/ecosystem/DESIGN_PRINCIPLES.md +9 -0
- data/docs/ecosystem/EXISTING_REPOS.md +31 -0
- data/docs/ecosystem/EXPERIMENTAL_LABS.md +24 -0
- data/docs/ecosystem/OVERVIEW.md +28 -0
- data/docs/ecosystem/RELEASE_ORDER.md +17 -0
- data/docs/observability/README.md +8 -0
- data/docs/rails/README.md +7 -0
- data/docs/rfcs/0001-stream-runtime.md +6 -0
- data/docs/rfcs/0002-schema-system.md +6 -0
- data/docs/rfcs/0003-observability-hooks.md +6 -0
- data/docs/rfcs/0004-async-runtime.md +6 -0
- data/docs/rfcs/README.md +16 -0
- data/docs/runtime/ERROR_CONTRACT.md +14 -0
- data/docs/runtime/SCHEMA_CONTRACT.md +28 -0
- data/docs/runtime/STREAM_CONTRACT.md +17 -0
- data/docs/runtime/STREAM_RUNTIME.md +20 -0
- data/docs/runtime/TRANSPORT_CONTRACT.md +26 -0
- data/docs/schema/README.md +8 -0
- data/docs/schema/STRUCTURED_OUTPUTS.md +22 -0
- data/docs/streaming/README.md +8 -0
- data/docs/testing/README.md +7 -0
- data/docs/testing/REPLAY_SYSTEM.md +17 -0
- data/docs/transport/README.md +7 -0
- data/examples/README.md +44 -0
- data/examples/basic_chat.rb +9 -0
- data/examples/cloud_models.rb +162 -0
- data/examples/embeddings.rb +10 -0
- data/examples/free_catalog.json +290 -0
- data/examples/generate.rb +9 -0
- data/examples/streaming.rb +12 -0
- data/examples/structured_tools.rb +90 -0
- data/examples/tool_calling_direct.rb +101 -0
- data/examples/tool_dto_example.rb +94 -0
- data/exe/ollama-client +5 -1
- data/lib/ollama/agent/executor.rb +243 -0
- data/lib/ollama/agent/messages.rb +31 -0
- data/lib/ollama/agent/planner.rb +45 -0
- data/lib/ollama/api_key_pool.rb +61 -0
- data/lib/ollama/attachment.rb +74 -0
- data/lib/ollama/capabilities.rb +1 -1
- data/lib/ollama/chat_response.rb +33 -0
- data/lib/ollama/client/chat/request_preparer.rb +82 -0
- data/lib/ollama/client/chat.rb +58 -68
- data/lib/ollama/client/chat_stream_processor.rb +85 -25
- data/lib/ollama/client/generate/request_preparer.rb +118 -0
- data/lib/ollama/client/generate/response_formatter.rb +95 -0
- data/lib/ollama/client/generate.rb +83 -165
- data/lib/ollama/client/model_management.rb +182 -84
- data/lib/ollama/client/openai_compat.rb +185 -0
- data/lib/ollama/client/raw.rb +66 -0
- data/lib/ollama/client/tool_intent.rb +38 -0
- data/lib/ollama/client/web_search.rb +39 -0
- data/lib/ollama/client.rb +83 -35
- data/lib/ollama/config.rb +122 -17
- data/lib/ollama/embeddings.rb +67 -29
- data/lib/ollama/errors.rb +55 -1
- data/lib/ollama/events.rb +55 -0
- data/lib/ollama/generate_stream_handler.rb +23 -4
- data/lib/ollama/http_error_handler.rb +42 -0
- data/lib/ollama/messages.rb +109 -0
- data/lib/ollama/middleware/cache.rb +73 -0
- data/lib/ollama/middleware/logger.rb +74 -0
- data/lib/ollama/middleware/metrics.rb +84 -0
- data/lib/ollama/middleware/tracing.rb +101 -0
- data/lib/ollama/middleware.rb +39 -0
- data/lib/ollama/model_profile.rb +1 -1
- data/lib/ollama/openai.rb +16 -0
- data/lib/ollama/options.rb +63 -1
- data/lib/ollama/params.rb +139 -0
- data/lib/ollama/parsers/base.rb +24 -0
- data/lib/ollama/parsers/chat.rb +22 -0
- data/lib/ollama/parsers/embeddings.rb +23 -0
- data/lib/ollama/parsers/generate.rb +38 -0
- data/lib/ollama/parsers/list_running.rb +15 -0
- data/lib/ollama/parsers/show_model.rb +14 -0
- data/lib/ollama/parsers/version.rb +15 -0
- data/lib/ollama/pipeline.rb +169 -0
- data/lib/ollama/plugins.rb +83 -0
- data/lib/ollama/policies/auto_pull.rb +69 -0
- data/lib/ollama/policies/base.rb +70 -0
- data/lib/ollama/policies/capability_validation.rb +88 -0
- data/lib/ollama/policies/fallback.rb +57 -0
- data/lib/ollama/policies/rate_limit.rb +115 -0
- data/lib/ollama/policies/repair_json.rb +137 -0
- data/lib/ollama/policies/retry/strategies/exponential.rb +27 -0
- data/lib/ollama/policies/retry/strategies/fixed.rb +27 -0
- data/lib/ollama/policies/retry/strategies/jitter.rb +30 -0
- data/lib/ollama/policies/retry/strategies/linear.rb +27 -0
- data/lib/ollama/policies/retry.rb +152 -0
- data/lib/ollama/policies/schema_repair.rb +180 -0
- data/lib/ollama/policies/timeout.rb +43 -0
- data/lib/ollama/policies.rb +24 -0
- data/lib/ollama/prompt.rb +86 -0
- data/lib/ollama/prompt_adapters/base.rb +3 -2
- data/lib/ollama/prompt_adapters/gemma4.rb +22 -14
- data/lib/ollama/prompts/tool_planner.rb +35 -0
- data/lib/ollama/providers/base.rb +70 -0
- data/lib/ollama/providers/llama_cpp.rb +131 -0
- data/lib/ollama/providers/ollama.rb +54 -0
- data/lib/ollama/providers/openai.rb +134 -0
- data/lib/ollama/providers.rb +28 -0
- data/lib/ollama/rate_limit_handler.rb +48 -0
- data/lib/ollama/request.rb +169 -0
- data/lib/ollama/response.rb +3 -2
- data/lib/ollama/responses/base.rb +11 -0
- data/lib/ollama/responses/chat.rb +11 -0
- data/lib/ollama/responses/embeddings.rb +14 -0
- data/lib/ollama/responses/generate.rb +22 -0
- data/lib/ollama/schema_dsl.rb +97 -0
- data/lib/ollama/schema_validator.rb +90 -53
- data/lib/ollama/schemas/tool_intent.json +15 -0
- data/lib/ollama/schemas/tool_intent.rb +16 -0
- data/lib/ollama/serializers/base.rb +44 -0
- data/lib/ollama/serializers/chat.rb +42 -0
- data/lib/ollama/serializers/embeddings.rb +36 -0
- data/lib/ollama/serializers/generate.rb +47 -0
- data/lib/ollama/streaming_observer.rb +22 -0
- data/lib/ollama/testing.rb +103 -0
- data/lib/ollama/tool/function/parameters/property.rb +72 -0
- data/lib/ollama/tool/function/parameters.rb +101 -0
- data/lib/ollama/tool/function.rb +78 -0
- data/lib/ollama/tool.rb +60 -0
- data/lib/ollama/tool_dsl.rb +93 -0
- data/lib/ollama/tool_intent.rb +19 -0
- data/lib/ollama/transport/base.rb +42 -0
- data/lib/ollama/transport/mock.rb +48 -0
- data/lib/ollama/transport/net_http.rb +76 -0
- data/lib/ollama/transport/request.rb +20 -0
- data/lib/ollama/transport/response.rb +41 -0
- data/lib/ollama/transport.rb +26 -0
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama_client.rb +35 -0
- data/script/live_branch_smoke/chat_exercises.rb +232 -0
- data/script/live_branch_smoke/generation_exercises.rb +128 -0
- data/script/live_branch_smoke/model_exercises.rb +105 -0
- data/script/live_branch_smoke/utility_exercises.rb +375 -0
- data/script/live_branch_smoke.rb +159 -174
- data/test_all_features.rb +315 -0
- metadata +158 -24
- data/.cursor/.gitignore +0 -1
- data/RELEASE_NOTES_v0.2.6.md +0 -41
- data/devagent_proper.rb +0 -430
- data/docs/TESTING.md +0 -508
- data/examples/agent_loop.rb +0 -120
- data/examples/failure_modes/invalid_json_repair.rb +0 -42
- data/examples/production/rails_agent.rb +0 -62
- data/market.jpg +0 -0
- data/print_capabilities.rb +0 -20
- data/schema.json +0 -1
- data/test_tool.rb +0 -26
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Stream Contract (Draft)
|
|
2
|
+
|
|
3
|
+
`ollama-client` is the canonical owner of stream semantics.
|
|
4
|
+
|
|
5
|
+
## Required runtime semantics
|
|
6
|
+
|
|
7
|
+
- Explicit lifecycle states
|
|
8
|
+
- Cancellation semantics (graceful vs immediate)
|
|
9
|
+
- UTF-8 safe chunk assembly
|
|
10
|
+
- SSE boundary correctness
|
|
11
|
+
- Backpressure and bounded buffering
|
|
12
|
+
- Error channel for malformed/incomplete events
|
|
13
|
+
|
|
14
|
+
## Contract direction
|
|
15
|
+
|
|
16
|
+
Higher-level gems (`ollama-stream`, `ollama-rails`, `ollama-agent`) compose on top of
|
|
17
|
+
core stream semantics and must not fork or redefine lifecycle guarantees.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Stream Runtime Design (Draft)
|
|
2
|
+
|
|
3
|
+
Target object:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
stream = client.chat.stream(...)
|
|
7
|
+
stream.each_token { |t| ... }
|
|
8
|
+
stream.each_chunk { |c| ... }
|
|
9
|
+
stream.cancel
|
|
10
|
+
stream.close
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Required semantics
|
|
14
|
+
|
|
15
|
+
- Explicit lifecycle: open -> active -> closing -> closed
|
|
16
|
+
- Cancellation modes: graceful vs immediate
|
|
17
|
+
- Backpressure: bounded buffering
|
|
18
|
+
- UTF-8 safe chunk assembly
|
|
19
|
+
- SSE event boundary correctness
|
|
20
|
+
- Error channel for malformed/incomplete events
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Transport Contract
|
|
2
|
+
|
|
3
|
+
`ollama-client` owns transport/runtime semantics.
|
|
4
|
+
|
|
5
|
+
## Adapter interface (current baseline)
|
|
6
|
+
|
|
7
|
+
- `request(uri:, request:, read_timeout:)`
|
|
8
|
+
- `stream(uri:, request:, &block)` (defined contract; implementation may be pending)
|
|
9
|
+
- `capabilities`
|
|
10
|
+
|
|
11
|
+
## Response contract
|
|
12
|
+
|
|
13
|
+
Adapters must return normalized transport responses with:
|
|
14
|
+
|
|
15
|
+
- `status`
|
|
16
|
+
- `headers`
|
|
17
|
+
- `body`
|
|
18
|
+
- `raw`
|
|
19
|
+
- `duration_ms`
|
|
20
|
+
|
|
21
|
+
Compatibility behavior for migration is allowed (e.g. status helper methods),
|
|
22
|
+
but response normalization remains mandatory.
|
|
23
|
+
|
|
24
|
+
## Ownership rule
|
|
25
|
+
|
|
26
|
+
No orchestration repo may redefine transport contracts. They consume this contract.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Structured Outputs (Draft)
|
|
2
|
+
|
|
3
|
+
## Goals
|
|
4
|
+
|
|
5
|
+
- typed schema declarations
|
|
6
|
+
- coercion + validation
|
|
7
|
+
- repair strategies
|
|
8
|
+
- deterministic parse contracts
|
|
9
|
+
|
|
10
|
+
## Target APIs
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
client.chat.parse(schema: TradeSignal)
|
|
14
|
+
client.chat.stream_parse(schema: TradeSignal)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Streaming requirements
|
|
18
|
+
|
|
19
|
+
- incremental parse
|
|
20
|
+
- partial AST assembly
|
|
21
|
+
- stream-time validation
|
|
22
|
+
- repair during stream
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Replay System (Draft)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Deterministic CI and incident reproduction for AI runtime behavior.
|
|
6
|
+
|
|
7
|
+
## Capabilities
|
|
8
|
+
|
|
9
|
+
- recorded fixtures
|
|
10
|
+
- response replay
|
|
11
|
+
- stream replay
|
|
12
|
+
- latency injection
|
|
13
|
+
- failure injection
|
|
14
|
+
|
|
15
|
+
## Integration point
|
|
16
|
+
|
|
17
|
+
Built on top of transport adapters (`:mock` first).
|
data/examples/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
This directory contains working example scripts demonstrating various capabilities of `ollama-client`.
|
|
4
|
+
|
|
5
|
+
## Running Examples
|
|
6
|
+
|
|
7
|
+
Most examples assume you are in the project root and use `require_relative` to load the local gem:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bundle exec ruby examples/<script>.rb
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
| Script | What it demonstrates |
|
|
16
|
+
|---|---|
|
|
17
|
+
| [`agent_loop.rb`](agent_loop.rb) | Agentic loop with tool calling and stateful memory |
|
|
18
|
+
| [`cloud_models.rb`](cloud_models.rb) | List Ollama Cloud models and probe which ones your account can access |
|
|
19
|
+
| [`llama_cpp_gpu_test.rb`](llama_cpp_gpu_test.rb) | Connect to a llama.cpp GPU server via the OpenAI provider |
|
|
20
|
+
| [`timeout_retry.rb`](timeout_retry.rb) | How timeout and exponential backoff work in production |
|
|
21
|
+
| [`failure_modes/invalid_json_repair.rb`](failure_modes/invalid_json_repair.rb) | Automatic JSON repair when the model returns markdown-wrapped JSON |
|
|
22
|
+
| [`production/rails_agent.rb`](production/rails_agent.rb) | Resilient background job pattern for Rails (e.g. Sidekiq) |
|
|
23
|
+
| [`dhanhq_agent.rb`](dhanhq_agent.rb) | DhanHQ trading agent runner |
|
|
24
|
+
| [`dhanhq/`](dhanhq/) | Modular DhanHQ technical analysis and trading agent system |
|
|
25
|
+
|
|
26
|
+
## Cloud Model Accessibility Probe
|
|
27
|
+
|
|
28
|
+
[`cloud_models.rb`](cloud_models.rb) is useful if you use Ollama Cloud and want to know which models are available under your current plan:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
export OLLAMA_API_KEY="your-ollama-cloud-api-key"
|
|
32
|
+
bundle exec ruby examples/cloud_models.rb
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Output is JSON printed to stdout:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
[
|
|
39
|
+
{ "name": "gpt-oss:20b", "accessible": true, "reason": null },
|
|
40
|
+
{ "name": "deepseek-v4-pro", "accessible": false, "reason": "plan_restricted" }
|
|
41
|
+
]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
See the script's header comments for the full list of `reason` codes.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Basic chat completion with Ollama
|
|
5
|
+
require "ollama_client"
|
|
6
|
+
|
|
7
|
+
client = Ollama::Client.new
|
|
8
|
+
response = client.chat(model: "llama3.1:8b", messages: [{ role: "user", content: "Hello, how are you?" }])
|
|
9
|
+
puts response
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../lib/ollama_client"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require "concurrent"
|
|
8
|
+
rescue LoadError
|
|
9
|
+
warn "Error: concurrent-ruby is required. Add it to your Gemfile."
|
|
10
|
+
exit 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# Cloud Model Accessibility Probe
|
|
15
|
+
#
|
|
16
|
+
# Lists all models available on Ollama Cloud and probes each one to determine
|
|
17
|
+
# whether the current authenticated account can run inference against it.
|
|
18
|
+
# Results are printed as JSON to stdout and saved to examples/free_catalog.json.
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
OUTPUT_FILE = File.join(__dir__, "free_catalog.json").freeze
|
|
22
|
+
ONLY_ACCESSIBLE = ARGV.include?("--accessible")
|
|
23
|
+
NAMES_ONLY = ARGV.include?("--names-only")
|
|
24
|
+
|
|
25
|
+
api_key = ENV.fetch("OLLAMA_API_KEY", nil)
|
|
26
|
+
if api_key.nil? || api_key.empty?
|
|
27
|
+
warn <<~USAGE
|
|
28
|
+
Usage: OLLAMA_API_KEY=<your-key> bundle exec ruby examples/cloud_models.rb
|
|
29
|
+
|
|
30
|
+
Please set the OLLAMA_API_KEY environment variable to your Ollama Cloud API key.
|
|
31
|
+
USAGE
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
config = Ollama::Config.new
|
|
36
|
+
config.base_url = "https://ollama.com"
|
|
37
|
+
config.api_key = api_key
|
|
38
|
+
config.timeout = 30
|
|
39
|
+
config.retries = 0
|
|
40
|
+
|
|
41
|
+
client = Ollama::Client.new(config: config)
|
|
42
|
+
|
|
43
|
+
# Fetch the public model catalog from Ollama Cloud.
|
|
44
|
+
begin
|
|
45
|
+
catalog = client.raw.get("/api/tags")
|
|
46
|
+
rescue Ollama::Error => e
|
|
47
|
+
warn "Failed to fetch model catalog: #{e.message}"
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
models = catalog["models"] || []
|
|
52
|
+
names = models.map { |m| m["name"] }.compact.sort
|
|
53
|
+
|
|
54
|
+
catalog_capabilities = {}
|
|
55
|
+
models.each do |m|
|
|
56
|
+
name = m["name"]
|
|
57
|
+
next unless name
|
|
58
|
+
|
|
59
|
+
catalog_capabilities[name] = Ollama::Capabilities.for(m)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Also try the OpenAI-compatible endpoint for a broader model list.
|
|
63
|
+
begin
|
|
64
|
+
openai_models = client.raw.get("/v1/models")
|
|
65
|
+
openai_names = (openai_models["data"] || []).map { |m| m["id"] }.compact
|
|
66
|
+
added = openai_names - names
|
|
67
|
+
names = (names + openai_names).uniq.sort
|
|
68
|
+
warn "/v1/models returned #{openai_names.length} model(s), #{added.length} new"
|
|
69
|
+
rescue Ollama::Error => e
|
|
70
|
+
warn "Warning: could not fetch /v1/models (#{e.message}), using /api/tags only"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Known Ollama Cloud models not always surfaced by /api/tags.
|
|
74
|
+
EXTRA_MODELS = %w[
|
|
75
|
+
gemma4:12b
|
|
76
|
+
gemma4:26b
|
|
77
|
+
gemma4:31b
|
|
78
|
+
qwen3.5:0.8b
|
|
79
|
+
qwen3.5:2b
|
|
80
|
+
qwen3.5:4b
|
|
81
|
+
qwen3.5:9b
|
|
82
|
+
qwen3.5:27b
|
|
83
|
+
qwen3.5:35b
|
|
84
|
+
qwen3.5:122b
|
|
85
|
+
mistral-large-3:123b
|
|
86
|
+
mistral-large-3:400b
|
|
87
|
+
nemotron-3-nano:4b
|
|
88
|
+
gemini-3-flash-preview
|
|
89
|
+
glm-5
|
|
90
|
+
kimi-k2.5
|
|
91
|
+
].freeze
|
|
92
|
+
|
|
93
|
+
extra = EXTRA_MODELS - names
|
|
94
|
+
names = (names + extra).uniq.sort
|
|
95
|
+
warn "Added #{extra.length} extra known cloud model(s) to probe"
|
|
96
|
+
|
|
97
|
+
if names.empty?
|
|
98
|
+
puts "[]"
|
|
99
|
+
exit 0
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
warn "Probing #{names.length} cloud model(s) with 10 concurrent threads..."
|
|
103
|
+
|
|
104
|
+
results = Concurrent::Array.new
|
|
105
|
+
pool = Concurrent::FixedThreadPool.new(10)
|
|
106
|
+
|
|
107
|
+
names.each do |name|
|
|
108
|
+
pool.post do
|
|
109
|
+
sleep(rand * 0.2)
|
|
110
|
+
|
|
111
|
+
result = { name: name, accessible: false, reason: nil, capabilities: catalog_capabilities[name] || {} }
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
client.chat(
|
|
115
|
+
model: name,
|
|
116
|
+
messages: [{ role: "user", content: "ping" }],
|
|
117
|
+
options: { num_predict: 1 }
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
result[:accessible] = true
|
|
121
|
+
rescue Ollama::UnauthorizedError
|
|
122
|
+
result[:reason] = "unauthorized"
|
|
123
|
+
rescue Ollama::ModelUnavailableError
|
|
124
|
+
result[:reason] = "unavailable"
|
|
125
|
+
rescue Ollama::NotFoundError
|
|
126
|
+
result[:reason] = "not_found"
|
|
127
|
+
rescue Ollama::HTTPError => e
|
|
128
|
+
reason = case e.status_code
|
|
129
|
+
when 402 then "usage_limit"
|
|
130
|
+
when 403 then "plan_restricted"
|
|
131
|
+
when 429 then "rate_limited"
|
|
132
|
+
else "http_error"
|
|
133
|
+
end
|
|
134
|
+
result[:reason] = reason
|
|
135
|
+
rescue Ollama::TimeoutError
|
|
136
|
+
result[:reason] = "timeout"
|
|
137
|
+
rescue StandardError => e
|
|
138
|
+
result[:reason] = "error: #{e.class}: #{e.message}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
results << result
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
pool.shutdown
|
|
146
|
+
pool.wait_for_termination
|
|
147
|
+
|
|
148
|
+
sorted = results.sort_by { |r| r[:name] }
|
|
149
|
+
|
|
150
|
+
if ONLY_ACCESSIBLE
|
|
151
|
+
sorted = sorted.select { |r| r[:accessible] }
|
|
152
|
+
warn "Filtering to #{sorted.length} accessible model(s) (--accessible)"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if NAMES_ONLY
|
|
156
|
+
sorted.each { |r| puts r[:name] }
|
|
157
|
+
else
|
|
158
|
+
json = JSON.pretty_generate(sorted)
|
|
159
|
+
puts json
|
|
160
|
+
File.write(OUTPUT_FILE, json + "\n")
|
|
161
|
+
warn "Saved free catalog to #{OUTPUT_FILE}"
|
|
162
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Generate embeddings
|
|
5
|
+
require "ollama_client"
|
|
6
|
+
|
|
7
|
+
client = Ollama::Client.new
|
|
8
|
+
response = client.embeddings.embed(model: "nomic-embed-text", input: "Hello, world!")
|
|
9
|
+
puts "Embedding dimension: #{response.length}"
|
|
10
|
+
puts "First 5 values: #{response.first(5)}"
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "deepseek-v4-flash",
|
|
4
|
+
"accessible": false,
|
|
5
|
+
"reason": "plan_restricted",
|
|
6
|
+
"capabilities": {
|
|
7
|
+
"tools": false,
|
|
8
|
+
"thinking": false,
|
|
9
|
+
"vision": false,
|
|
10
|
+
"embeddings": false
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "deepseek-v4-flash:0731",
|
|
15
|
+
"accessible": false,
|
|
16
|
+
"reason": "plan_restricted",
|
|
17
|
+
"capabilities": {
|
|
18
|
+
"tools": false,
|
|
19
|
+
"thinking": false,
|
|
20
|
+
"vision": false,
|
|
21
|
+
"embeddings": false
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "deepseek-v4-pro",
|
|
26
|
+
"accessible": false,
|
|
27
|
+
"reason": "plan_restricted",
|
|
28
|
+
"capabilities": {
|
|
29
|
+
"tools": false,
|
|
30
|
+
"thinking": false,
|
|
31
|
+
"vision": false,
|
|
32
|
+
"embeddings": false
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "gemini-3-flash-preview",
|
|
37
|
+
"accessible": false,
|
|
38
|
+
"reason": "http_error",
|
|
39
|
+
"capabilities": {}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "gemma4:12b",
|
|
43
|
+
"accessible": false,
|
|
44
|
+
"reason": "not_found",
|
|
45
|
+
"capabilities": {}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "gemma4:26b",
|
|
49
|
+
"accessible": false,
|
|
50
|
+
"reason": "not_found",
|
|
51
|
+
"capabilities": {}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "gemma4:31b",
|
|
55
|
+
"accessible": true,
|
|
56
|
+
"reason": null,
|
|
57
|
+
"capabilities": {
|
|
58
|
+
"tools": false,
|
|
59
|
+
"thinking": true,
|
|
60
|
+
"vision": false,
|
|
61
|
+
"embeddings": false
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "glm-5",
|
|
66
|
+
"accessible": false,
|
|
67
|
+
"reason": "http_error",
|
|
68
|
+
"capabilities": {}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "glm-5.1",
|
|
72
|
+
"accessible": false,
|
|
73
|
+
"reason": "plan_restricted",
|
|
74
|
+
"capabilities": {
|
|
75
|
+
"tools": false,
|
|
76
|
+
"thinking": false,
|
|
77
|
+
"vision": false,
|
|
78
|
+
"embeddings": false
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "glm-5.2",
|
|
83
|
+
"accessible": false,
|
|
84
|
+
"reason": "plan_restricted",
|
|
85
|
+
"capabilities": {
|
|
86
|
+
"tools": false,
|
|
87
|
+
"thinking": false,
|
|
88
|
+
"vision": false,
|
|
89
|
+
"embeddings": false
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "gpt-oss:120b",
|
|
94
|
+
"accessible": true,
|
|
95
|
+
"reason": null,
|
|
96
|
+
"capabilities": {
|
|
97
|
+
"tools": false,
|
|
98
|
+
"thinking": false,
|
|
99
|
+
"vision": false,
|
|
100
|
+
"embeddings": false
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"name": "gpt-oss:20b",
|
|
105
|
+
"accessible": true,
|
|
106
|
+
"reason": null,
|
|
107
|
+
"capabilities": {
|
|
108
|
+
"tools": false,
|
|
109
|
+
"thinking": false,
|
|
110
|
+
"vision": false,
|
|
111
|
+
"embeddings": false
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "kimi-k2.5",
|
|
116
|
+
"accessible": false,
|
|
117
|
+
"reason": "http_error",
|
|
118
|
+
"capabilities": {}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "kimi-k2.6",
|
|
122
|
+
"accessible": false,
|
|
123
|
+
"reason": "plan_restricted",
|
|
124
|
+
"capabilities": {
|
|
125
|
+
"tools": false,
|
|
126
|
+
"thinking": false,
|
|
127
|
+
"vision": false,
|
|
128
|
+
"embeddings": false
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"name": "kimi-k2.7-code",
|
|
133
|
+
"accessible": false,
|
|
134
|
+
"reason": "plan_restricted",
|
|
135
|
+
"capabilities": {
|
|
136
|
+
"tools": false,
|
|
137
|
+
"thinking": false,
|
|
138
|
+
"vision": false,
|
|
139
|
+
"embeddings": false
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "kimi-k3",
|
|
144
|
+
"accessible": false,
|
|
145
|
+
"reason": "plan_restricted",
|
|
146
|
+
"capabilities": {
|
|
147
|
+
"tools": false,
|
|
148
|
+
"thinking": false,
|
|
149
|
+
"vision": false,
|
|
150
|
+
"embeddings": false
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"name": "minimax-m2.7",
|
|
155
|
+
"accessible": false,
|
|
156
|
+
"reason": "plan_restricted",
|
|
157
|
+
"capabilities": {
|
|
158
|
+
"tools": false,
|
|
159
|
+
"thinking": false,
|
|
160
|
+
"vision": false,
|
|
161
|
+
"embeddings": false
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "minimax-m3",
|
|
166
|
+
"accessible": true,
|
|
167
|
+
"reason": null,
|
|
168
|
+
"capabilities": {
|
|
169
|
+
"tools": false,
|
|
170
|
+
"thinking": false,
|
|
171
|
+
"vision": false,
|
|
172
|
+
"embeddings": false
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": "mistral-large-3:123b",
|
|
177
|
+
"accessible": false,
|
|
178
|
+
"reason": "not_found",
|
|
179
|
+
"capabilities": {}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "mistral-large-3:400b",
|
|
183
|
+
"accessible": false,
|
|
184
|
+
"reason": "not_found",
|
|
185
|
+
"capabilities": {}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "mistral-large-3:675b",
|
|
189
|
+
"accessible": false,
|
|
190
|
+
"reason": "plan_restricted",
|
|
191
|
+
"capabilities": {
|
|
192
|
+
"tools": false,
|
|
193
|
+
"thinking": false,
|
|
194
|
+
"vision": false,
|
|
195
|
+
"embeddings": false
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"name": "nemotron-3-nano:30b",
|
|
200
|
+
"accessible": true,
|
|
201
|
+
"reason": null,
|
|
202
|
+
"capabilities": {
|
|
203
|
+
"tools": false,
|
|
204
|
+
"thinking": false,
|
|
205
|
+
"vision": false,
|
|
206
|
+
"embeddings": false
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"name": "nemotron-3-nano:4b",
|
|
211
|
+
"accessible": false,
|
|
212
|
+
"reason": "not_found",
|
|
213
|
+
"capabilities": {}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "nemotron-3-super",
|
|
217
|
+
"accessible": true,
|
|
218
|
+
"reason": null,
|
|
219
|
+
"capabilities": {
|
|
220
|
+
"tools": false,
|
|
221
|
+
"thinking": false,
|
|
222
|
+
"vision": false,
|
|
223
|
+
"embeddings": false
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "nemotron-3-ultra",
|
|
228
|
+
"accessible": true,
|
|
229
|
+
"reason": null,
|
|
230
|
+
"capabilities": {
|
|
231
|
+
"tools": false,
|
|
232
|
+
"thinking": false,
|
|
233
|
+
"vision": false,
|
|
234
|
+
"embeddings": false
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"name": "qwen3.5:0.8b",
|
|
239
|
+
"accessible": false,
|
|
240
|
+
"reason": "not_found",
|
|
241
|
+
"capabilities": {}
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"name": "qwen3.5:122b",
|
|
245
|
+
"accessible": false,
|
|
246
|
+
"reason": "rate_limited",
|
|
247
|
+
"capabilities": {}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "qwen3.5:27b",
|
|
251
|
+
"accessible": false,
|
|
252
|
+
"reason": "rate_limited",
|
|
253
|
+
"capabilities": {}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "qwen3.5:2b",
|
|
257
|
+
"accessible": false,
|
|
258
|
+
"reason": "rate_limited",
|
|
259
|
+
"capabilities": {}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "qwen3.5:35b",
|
|
263
|
+
"accessible": false,
|
|
264
|
+
"reason": "not_found",
|
|
265
|
+
"capabilities": {}
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"name": "qwen3.5:397b",
|
|
269
|
+
"accessible": false,
|
|
270
|
+
"reason": "plan_restricted",
|
|
271
|
+
"capabilities": {
|
|
272
|
+
"tools": false,
|
|
273
|
+
"thinking": false,
|
|
274
|
+
"vision": false,
|
|
275
|
+
"embeddings": false
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"name": "qwen3.5:4b",
|
|
280
|
+
"accessible": false,
|
|
281
|
+
"reason": "not_found",
|
|
282
|
+
"capabilities": {}
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"name": "qwen3.5:9b",
|
|
286
|
+
"accessible": false,
|
|
287
|
+
"reason": "rate_limited",
|
|
288
|
+
"capabilities": {}
|
|
289
|
+
}
|
|
290
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Single completion (generate endpoint)
|
|
5
|
+
require "ollama_client"
|
|
6
|
+
|
|
7
|
+
client = Ollama::Client.new
|
|
8
|
+
response = client.generate(prompt: "Explain quantum computing in one sentence", model: "llama3.1:8b")
|
|
9
|
+
puts response
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Streaming chat completion
|
|
5
|
+
require "ollama_client"
|
|
6
|
+
|
|
7
|
+
client = Ollama::Client.new
|
|
8
|
+
client.chat(model: "llama3.1:8b", messages: [{ role: "user", content: "Count from 1 to 5" }], stream: true) do |chunk|
|
|
9
|
+
print chunk.dig("message", "content")
|
|
10
|
+
$stdout.flush
|
|
11
|
+
end
|
|
12
|
+
puts
|