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.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +11 -0
  3. data/API_CONTRACT.md +79 -8
  4. data/CHANGELOG.md +45 -0
  5. data/CONTRIBUTING.md +8 -0
  6. data/README.md +205 -273
  7. data/ROADMAP.md +23 -0
  8. data/docs/API_GAPS.md +18 -141
  9. data/docs/ARCHITECTURE.md +141 -0
  10. data/docs/AREAS_FOR_CONSIDERATION.md +13 -1
  11. data/docs/CLOUD.md +19 -0
  12. data/docs/CONSOLE_IMPROVEMENTS.md +20 -1
  13. data/docs/ECOSYSTEM_GEMS.md +41 -0
  14. data/docs/ECOSYSTEM_STRATEGY.md +151 -0
  15. data/docs/GETTING_STARTED.md +16 -8
  16. data/docs/INTEGRATION_TESTING.md +23 -5
  17. data/docs/PRODUCTION_FIXES.md +15 -3
  18. data/docs/QUICK_START.md +1 -1
  19. data/docs/README.md +1 -0
  20. data/docs/RUBYLLM_ADOPTION_MATRIX.md +273 -0
  21. data/docs/adr/001-openai-boundary.md +12 -0
  22. data/docs/adr/002-transport-abstraction.md +12 -0
  23. data/docs/adr/003-response-normalization.md +12 -0
  24. data/docs/adr/004-mock-transport.md +16 -0
  25. data/docs/adr/005-error-taxonomy.md +16 -0
  26. data/docs/adr/006-stream-runtime.md +16 -0
  27. data/docs/ecosystem/BOUNDARIES.md +19 -0
  28. data/docs/ecosystem/DEPENDENCY_GRAPH.md +14 -0
  29. data/docs/ecosystem/DESIGN_PRINCIPLES.md +9 -0
  30. data/docs/ecosystem/EXISTING_REPOS.md +31 -0
  31. data/docs/ecosystem/EXPERIMENTAL_LABS.md +24 -0
  32. data/docs/ecosystem/OVERVIEW.md +28 -0
  33. data/docs/ecosystem/RELEASE_ORDER.md +17 -0
  34. data/docs/observability/README.md +8 -0
  35. data/docs/rails/README.md +7 -0
  36. data/docs/rfcs/0001-stream-runtime.md +6 -0
  37. data/docs/rfcs/0002-schema-system.md +6 -0
  38. data/docs/rfcs/0003-observability-hooks.md +6 -0
  39. data/docs/rfcs/0004-async-runtime.md +6 -0
  40. data/docs/rfcs/README.md +16 -0
  41. data/docs/runtime/ERROR_CONTRACT.md +14 -0
  42. data/docs/runtime/SCHEMA_CONTRACT.md +28 -0
  43. data/docs/runtime/STREAM_CONTRACT.md +17 -0
  44. data/docs/runtime/STREAM_RUNTIME.md +20 -0
  45. data/docs/runtime/TRANSPORT_CONTRACT.md +26 -0
  46. data/docs/schema/README.md +8 -0
  47. data/docs/schema/STRUCTURED_OUTPUTS.md +22 -0
  48. data/docs/streaming/README.md +8 -0
  49. data/docs/testing/README.md +7 -0
  50. data/docs/testing/REPLAY_SYSTEM.md +17 -0
  51. data/docs/transport/README.md +7 -0
  52. data/examples/README.md +44 -0
  53. data/examples/basic_chat.rb +9 -0
  54. data/examples/cloud_models.rb +162 -0
  55. data/examples/embeddings.rb +10 -0
  56. data/examples/free_catalog.json +290 -0
  57. data/examples/generate.rb +9 -0
  58. data/examples/streaming.rb +12 -0
  59. data/examples/structured_tools.rb +90 -0
  60. data/examples/tool_calling_direct.rb +101 -0
  61. data/examples/tool_dto_example.rb +94 -0
  62. data/exe/ollama-client +5 -1
  63. data/lib/ollama/agent/executor.rb +243 -0
  64. data/lib/ollama/agent/messages.rb +31 -0
  65. data/lib/ollama/agent/planner.rb +45 -0
  66. data/lib/ollama/api_key_pool.rb +61 -0
  67. data/lib/ollama/attachment.rb +74 -0
  68. data/lib/ollama/capabilities.rb +1 -1
  69. data/lib/ollama/chat_response.rb +33 -0
  70. data/lib/ollama/client/chat/request_preparer.rb +82 -0
  71. data/lib/ollama/client/chat.rb +58 -68
  72. data/lib/ollama/client/chat_stream_processor.rb +85 -25
  73. data/lib/ollama/client/generate/request_preparer.rb +118 -0
  74. data/lib/ollama/client/generate/response_formatter.rb +95 -0
  75. data/lib/ollama/client/generate.rb +83 -165
  76. data/lib/ollama/client/model_management.rb +182 -84
  77. data/lib/ollama/client/openai_compat.rb +185 -0
  78. data/lib/ollama/client/raw.rb +66 -0
  79. data/lib/ollama/client/tool_intent.rb +38 -0
  80. data/lib/ollama/client/web_search.rb +39 -0
  81. data/lib/ollama/client.rb +83 -35
  82. data/lib/ollama/config.rb +122 -17
  83. data/lib/ollama/embeddings.rb +67 -29
  84. data/lib/ollama/errors.rb +55 -1
  85. data/lib/ollama/events.rb +55 -0
  86. data/lib/ollama/generate_stream_handler.rb +23 -4
  87. data/lib/ollama/http_error_handler.rb +42 -0
  88. data/lib/ollama/messages.rb +109 -0
  89. data/lib/ollama/middleware/cache.rb +73 -0
  90. data/lib/ollama/middleware/logger.rb +74 -0
  91. data/lib/ollama/middleware/metrics.rb +84 -0
  92. data/lib/ollama/middleware/tracing.rb +101 -0
  93. data/lib/ollama/middleware.rb +39 -0
  94. data/lib/ollama/model_profile.rb +1 -1
  95. data/lib/ollama/openai.rb +16 -0
  96. data/lib/ollama/options.rb +63 -1
  97. data/lib/ollama/params.rb +139 -0
  98. data/lib/ollama/parsers/base.rb +24 -0
  99. data/lib/ollama/parsers/chat.rb +22 -0
  100. data/lib/ollama/parsers/embeddings.rb +23 -0
  101. data/lib/ollama/parsers/generate.rb +38 -0
  102. data/lib/ollama/parsers/list_running.rb +15 -0
  103. data/lib/ollama/parsers/show_model.rb +14 -0
  104. data/lib/ollama/parsers/version.rb +15 -0
  105. data/lib/ollama/pipeline.rb +169 -0
  106. data/lib/ollama/plugins.rb +83 -0
  107. data/lib/ollama/policies/auto_pull.rb +69 -0
  108. data/lib/ollama/policies/base.rb +70 -0
  109. data/lib/ollama/policies/capability_validation.rb +88 -0
  110. data/lib/ollama/policies/fallback.rb +57 -0
  111. data/lib/ollama/policies/rate_limit.rb +115 -0
  112. data/lib/ollama/policies/repair_json.rb +137 -0
  113. data/lib/ollama/policies/retry/strategies/exponential.rb +27 -0
  114. data/lib/ollama/policies/retry/strategies/fixed.rb +27 -0
  115. data/lib/ollama/policies/retry/strategies/jitter.rb +30 -0
  116. data/lib/ollama/policies/retry/strategies/linear.rb +27 -0
  117. data/lib/ollama/policies/retry.rb +152 -0
  118. data/lib/ollama/policies/schema_repair.rb +180 -0
  119. data/lib/ollama/policies/timeout.rb +43 -0
  120. data/lib/ollama/policies.rb +24 -0
  121. data/lib/ollama/prompt.rb +86 -0
  122. data/lib/ollama/prompt_adapters/base.rb +3 -2
  123. data/lib/ollama/prompt_adapters/gemma4.rb +22 -14
  124. data/lib/ollama/prompts/tool_planner.rb +35 -0
  125. data/lib/ollama/providers/base.rb +70 -0
  126. data/lib/ollama/providers/llama_cpp.rb +131 -0
  127. data/lib/ollama/providers/ollama.rb +54 -0
  128. data/lib/ollama/providers/openai.rb +134 -0
  129. data/lib/ollama/providers.rb +28 -0
  130. data/lib/ollama/rate_limit_handler.rb +48 -0
  131. data/lib/ollama/request.rb +169 -0
  132. data/lib/ollama/response.rb +3 -2
  133. data/lib/ollama/responses/base.rb +11 -0
  134. data/lib/ollama/responses/chat.rb +11 -0
  135. data/lib/ollama/responses/embeddings.rb +14 -0
  136. data/lib/ollama/responses/generate.rb +22 -0
  137. data/lib/ollama/schema_dsl.rb +97 -0
  138. data/lib/ollama/schema_validator.rb +90 -53
  139. data/lib/ollama/schemas/tool_intent.json +15 -0
  140. data/lib/ollama/schemas/tool_intent.rb +16 -0
  141. data/lib/ollama/serializers/base.rb +44 -0
  142. data/lib/ollama/serializers/chat.rb +42 -0
  143. data/lib/ollama/serializers/embeddings.rb +36 -0
  144. data/lib/ollama/serializers/generate.rb +47 -0
  145. data/lib/ollama/streaming_observer.rb +22 -0
  146. data/lib/ollama/testing.rb +103 -0
  147. data/lib/ollama/tool/function/parameters/property.rb +72 -0
  148. data/lib/ollama/tool/function/parameters.rb +101 -0
  149. data/lib/ollama/tool/function.rb +78 -0
  150. data/lib/ollama/tool.rb +60 -0
  151. data/lib/ollama/tool_dsl.rb +93 -0
  152. data/lib/ollama/tool_intent.rb +19 -0
  153. data/lib/ollama/transport/base.rb +42 -0
  154. data/lib/ollama/transport/mock.rb +48 -0
  155. data/lib/ollama/transport/net_http.rb +76 -0
  156. data/lib/ollama/transport/request.rb +20 -0
  157. data/lib/ollama/transport/response.rb +41 -0
  158. data/lib/ollama/transport.rb +26 -0
  159. data/lib/ollama/version.rb +1 -1
  160. data/lib/ollama_client.rb +35 -0
  161. data/script/live_branch_smoke/chat_exercises.rb +232 -0
  162. data/script/live_branch_smoke/generation_exercises.rb +128 -0
  163. data/script/live_branch_smoke/model_exercises.rb +105 -0
  164. data/script/live_branch_smoke/utility_exercises.rb +375 -0
  165. data/script/live_branch_smoke.rb +159 -174
  166. data/test_all_features.rb +315 -0
  167. metadata +158 -24
  168. data/.cursor/.gitignore +0 -1
  169. data/RELEASE_NOTES_v0.2.6.md +0 -41
  170. data/devagent_proper.rb +0 -430
  171. data/docs/TESTING.md +0 -508
  172. data/examples/agent_loop.rb +0 -120
  173. data/examples/failure_modes/invalid_json_repair.rb +0 -42
  174. data/examples/production/rails_agent.rb +0 -62
  175. data/market.jpg +0 -0
  176. data/print_capabilities.rb +0 -20
  177. data/schema.json +0 -1
  178. data/test_tool.rb +0 -26
data/docs/API_GAPS.md CHANGED
@@ -1,143 +1,20 @@
1
1
  # Gaps: Ollama REST API vs ollama-client Gem
2
2
 
3
- Comparison against the [Ollama API Reference](https://ollama.readthedocs.io/en/api/). The Postman collection at `c:\Users\shubh\Downloads\Ollama REST API.postman_collection.json` was not in the workspace; add it to the repo to diff against it as well.
4
-
5
- ---
6
-
7
- ## 1. Blob API β€” **Not implemented**
8
-
9
- The API defines two blob endpoints for remote model creation:
10
-
11
- | Endpoint | Method | Purpose |
12
- |----------|--------|---------|
13
- | `/api/blobs/:digest` | HEAD | Check if a blob exists (200 vs 404) |
14
- | `/api/blobs/:digest` | POST | Create a blob (upload file; returns server path) |
15
-
16
- **Gap:** No `blob_exists?(digest)` or `create_blob(digest, file)` in the gem. Required for remote create when using `FROM`/`ADAPTER` with blobs.
17
-
18
- ---
19
-
20
- ## 2. Create Model β€” **Parameter mismatch**
21
-
22
- **API parameters:** `model`, `modelfile` (optional), `path` (optional), `stream`, `quantize`.
23
-
24
- **Gem:** `create_model(model:, from:, system:, template:, license:, parameters:, messages:, quantize:, stream:)` and sends `from` (+ system, template, etc.) in the JSON body.
25
-
26
- - The reference docs do not list `from`, `system`, or `template` as top-level create parameters; they describe passing **modelfile content** or a **path** to a Modelfile.
27
- - If the Ollama server accepts `from` as a shorthand for a minimal modelfile, the gem may work but is undocumented in the official API.
28
- - **Gaps:**
29
- - No way to pass raw **modelfile** string (full Modelfile content).
30
- - No way to pass **path** (path to a Modelfile on the server).
31
-
32
- **Recommendation:** Add optional `modelfile:` and `path:` to `create_model`. If `modelfile` or `path` is present, use that; otherwise keep building from `from` + system/template for backward compatibility (and document that behavior).
33
-
34
- ---
35
-
36
- ## 3. Pull β€” **Missing parameters**
37
-
38
- **API parameters:** `model`, `stream` (optional), `insecure` (optional).
39
-
40
- **Gem:** `pull(model_name)` β€” always sends `stream: false` and does not accept `insecure` or `stream`.
41
-
42
- **Gaps:**
43
- - No `insecure:` option (e.g. for pulling from a custom/self-hosted registry).
44
- - No choice of streaming vs non-streaming; callers cannot get progress via stream.
45
-
46
- **Recommendation:** Add `pull(model_name, stream: false, insecure: false)` and pass them through.
47
-
48
- ---
49
-
50
- ## 4. Generate β€” **Context (conversational memory)**
51
-
52
- **API:** `/api/generate` accepts optional `context` (from a previous response) and returns `context` in the response for use in the next request.
53
-
54
- **Gem:** `generate` does not accept or return `context`. Multi-turn β€œmemory” is only via chat, not via generate.
55
-
56
- **Gap:** No support for generate-side context for short conversational memory when using the generate endpoint.
57
-
58
- **Recommendation:** Optional `context:` argument and returning `context` in metadata when `return_meta: true` (or a dedicated accessor) would align with the API.
59
-
60
- ---
61
-
62
- ## 5. Generate / Chat β€” **Options coverage**
63
-
64
- **API (Modelfile/options):** The docs list many options, including at least:
65
-
66
- - `min_p`
67
- - `penalize_newline`
68
- - `repeat_last_n`
69
- - `numa`
70
- - `num_batch`
71
- - `main_gpu`
72
- - `low_vram`
73
- - `vocab_only`
74
- - `use_mmap`
75
- - `use_mlock`
76
-
77
- **Gem:** `Ollama::Options` supports: `temperature`, `top_p`, `top_k`, `num_ctx`, `repeat_penalty`, `seed`, `num_predict`, `stop`, `tfs_z`, `mirostat`, `mirostat_tau`, `mirostat_eta`, `num_gpu`, `num_thread`, `num_keep`, `typical_p`, `presence_penalty`, `frequency_penalty`.
78
-
79
- **Gap:** Options such as `min_p`, `penalize_newline`, `repeat_last_n`, `numa`, `num_batch`, `main_gpu`, `low_vram`, `vocab_only`, `use_mmap`, `use_mlock` are not in the type-safe Options class. They can still be passed as a raw hash via `options:`, so behavior is only a **documentation/consistency** gap.
80
-
81
- **Recommendation:** Either extend `Ollama::Options` with these keys (and document which are server-dependent) or explicitly document that arbitrary option hashes are allowed and list the known API options in the yardocs.
82
-
83
- ---
84
-
85
- ## 6. Push β€” **Streaming behavior**
86
-
87
- **API:** Supports `stream: true` (default) or `stream: false` with a single final JSON object.
88
-
89
- **Gem:** `push_model(model:, insecure: false, stream: false)` β€” implements both parameters. When `stream: true`, the current implementation uses a single `http_request` and `JSON.parse(res.body)`, which is wrong for streaming NDJSON.
90
-
91
- **Gap:** For `stream: true`, the gem would need to read the response body as a stream (NDJSON) and yield or return status updates, similar to pull/generate streaming. Currently only non-streaming push is correct.
92
-
93
- ---
94
-
95
- ## 7. Pull β€” **Streaming behavior**
96
-
97
- **API:** Pull can stream progress (default) or return a single object when `stream: false`.
98
-
99
- **Gem:** Always sends `stream: false` and parses a single JSON object. No streaming pull.
100
-
101
- **Gap:** No API to get pull progress (e.g. status, digest, total, completed) via streaming. This is a **feature** gap, not a wrong-call gap.
102
-
103
- ---
104
-
105
- ## 8. Chat β€” **Tool message role**
106
-
107
- **API:** Message object may have `role`: `system` | `user` | `assistant` | `tool`. Tool results are sent as messages with `role: "tool"`.
108
-
109
- **Gem:** Response wrapper and tool_calls handling are in place; no gap identified for normal tool use. If the client ever needs to **send** tool results back (e.g. in a follow-up request), the message format with `role: "tool"` should be documented or helpers provided. Not a missing endpoint, but worth documenting for agent loops.
110
-
111
- ---
112
-
113
- ## 9. Embeddings β€” **Legacy endpoint**
114
-
115
- **API:** Documents both `/api/embed` (current) and `/api/embeddings` (superseded).
116
-
117
- **Gem:** Uses `/api/embed` only. No gap.
118
-
119
- ---
120
-
121
- ## 10. Load / Unload model (explicit)
122
-
123
- **API:** Describes loading by calling generate/chat with empty prompt (and optional `keep_alive`), and unload with empty prompt + `keep_alive: 0`.
124
-
125
- **Gem:** No dedicated `load_model` or `unload_model` helpers. Callers can do the same by calling `generate(prompt: "", ...)` or `chat(messages: [], ...)` with `keep_alive`.
126
-
127
- **Gap:** Convenience only β€” optional helpers could reduce confusion and document the pattern.
128
-
129
- ---
130
-
131
- ## Summary table
132
-
133
- | Area | Severity | Gap |
134
- |-------------------|------------|-----|
135
- | Blob API | High | HEAD/POST blobs not implemented |
136
- | Create model | Medium | No raw `modelfile` or `path`; uses `from` only |
137
- | Pull | Medium | No `insecure` or `stream`; no streaming progress |
138
- | Push | Low | `stream: true` not implemented as real streaming |
139
- | Generate context | Low | No `context` in/out for conversational memory |
140
- | Options | Low | Several Modelfile options not in `Ollama::Options` |
141
- | Load/Unload | Optional | No convenience methods for load/unload |
142
-
143
- Implementing **Blob API** and **Create model (modelfile/path)** would fully align the gem with the documented API for model creation. **Pull (insecure + stream)** and **Generate context** are the next most useful for parity and agent use cases.
3
+ Original comparison against the [Ollama API Reference](https://ollama.readthedocs.io/en/api/). All 10
4
+ items identified below have since been closed. This file is kept for history; the actively maintained
5
+ gap tracker is [`docs/RUBYLLM_ADOPTION_MATRIX.md`](RUBYLLM_ADOPTION_MATRIX.md) section K.
6
+
7
+ | # | Area | Original gap | Status |
8
+ |---|---|---|---|
9
+ | 1 | Blob API | No `blob_exists?`/`create_blob` | **Resolved** β€” `Client::ModelManagement#blob_exists?(digest:)`, `#create_blob(digest:, content:)` |
10
+ | 2 | Create model | No raw `modelfile`/`path` | **Resolved** β€” `create_model(model:, from:, modelfile:, path:, ...)` |
11
+ | 3 | Pull | No `insecure`/`stream` | **Resolved** β€” `pull(model_name, insecure:, stream:, hooks:)` |
12
+ | 4 | Generate context | No `context` in/out | **Resolved** β€” `generate(prompt:, context:, ...)`, context returned via `return_meta:` |
13
+ | 5 | Options coverage | Missing `min_p`, `penalize_newline`, `repeat_last_n`, `numa`, `num_batch`, `main_gpu`, `low_vram`, `vocab_only`, `use_mmap`, `use_mlock` | **Resolved** β€” all present on `Ollama::Options` |
14
+ | 6 | Push streaming | `stream: true` not real NDJSON streaming | **Resolved** β€” `push_model` streams via `handle_ndjson_stream` + `hooks[:on_progress]` |
15
+ | 7 | Pull streaming | No streaming progress | **Resolved** β€” same mechanism as push |
16
+ | 8 | Tool message role | No `role: "tool"` helper for follow-up requests | **Resolved** β€” `Ollama::Agent::Messages#tool_result` (`lib/ollama/agent/messages.rb`) |
17
+ | 9 | Embeddings legacy endpoint | n/a | No gap β€” `/api/embed` is the current documented endpoint; `/api/embeddings` is Ollama's deprecated alias |
18
+ | 10 | Load/unload convenience | No dedicated helpers | **Resolved** β€” `load_model(model:, keep_alive:)`, `unload_model(model:)` |
19
+
20
+ See `API_CONTRACT.md` for the current signatures of all methods above.
@@ -0,0 +1,141 @@
1
+ # ollama-client Architecture
2
+
3
+ This document specifies the frozen v1 architecture design of `ollama-client`.
4
+
5
+ ```text
6
+ ollama-client
7
+ β”‚
8
+ β”œβ”€β”€ Config
9
+ β”œβ”€β”€ Request
10
+ β”œβ”€β”€ Response
11
+ β”œβ”€β”€ Errors
12
+ β”‚
13
+ β”œβ”€β”€ Transport
14
+ β”‚ β”œβ”€β”€ HTTP
15
+ β”‚ β”œβ”€β”€ Mock
16
+ β”‚ └── Streaming
17
+ β”‚
18
+ β”œβ”€β”€ Middleware
19
+ β”‚ β”œβ”€β”€ Logger
20
+ β”‚ β”œβ”€β”€ Metrics
21
+ β”‚ β”œβ”€β”€ Cache
22
+ β”‚ β”œβ”€β”€ Tracing
23
+ β”‚ └── Base
24
+ β”‚
25
+ β”œβ”€β”€ Policies
26
+ β”‚ β”œβ”€β”€ Retry
27
+ β”‚ β”œβ”€β”€ AutoPull
28
+ β”‚ β”œβ”€β”€ RepairJson
29
+ β”‚ β”œβ”€β”€ SchemaRepair
30
+ β”‚ β”œβ”€β”€ CapabilityValidation
31
+ β”‚ └── RateLimit
32
+ β”‚
33
+ β”œβ”€β”€ Core
34
+ β”‚ β”œβ”€β”€ Chat
35
+ β”‚ β”œβ”€β”€ Generate
36
+ β”‚ β”œβ”€β”€ Embeddings
37
+ β”‚ β”œβ”€β”€ Models
38
+ β”‚ └── Version
39
+ β”‚
40
+ β”œβ”€β”€ Ruby
41
+ β”‚ β”œβ”€β”€ Messages
42
+ β”‚ β”œβ”€β”€ Tool
43
+ β”‚ β”œβ”€β”€ Schema
44
+ β”‚ β”œβ”€β”€ TypedResponses
45
+ β”‚ β”œβ”€β”€ Streaming
46
+ β”‚ └── Testing
47
+ β”‚
48
+ β”œβ”€β”€ Plugins
49
+ β”‚
50
+ β”œβ”€β”€ Rails (optional)
51
+ β”‚
52
+ └── Compliance Tests
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 1. Request/Response Pipeline Design
58
+
59
+ All lifecycle hooks, logging, retries, and errors run inside a single composable pipeline of middleware:
60
+
61
+ ```text
62
+ Request
63
+ β”‚
64
+ β–Ό
65
+ Middleware (Logger / Caching)
66
+ β”‚
67
+ β–Ό
68
+ Policies (Retry / AutoPull / JsonRepair)
69
+ β”‚
70
+ β–Ό
71
+ Transport Terminal (HTTP / Mock)
72
+ β”‚
73
+ β–Ό
74
+ Response
75
+ β”‚
76
+ β–Ό
77
+ Typed Objects
78
+ ```
79
+
80
+ ### Composable Middleware Stack
81
+
82
+ Every middleware implements the following Rack/Faraday-style interface:
83
+
84
+ ```ruby
85
+ class Base
86
+ def initialize(app, **options)
87
+ @app = app
88
+ @options = options
89
+ end
90
+
91
+ def call(request)
92
+ @app.call(request)
93
+ end
94
+ end
95
+ ```
96
+
97
+ ---
98
+
99
+ ## 2. Core Abstractions
100
+
101
+ ### Request Object
102
+
103
+ An immutable container for all request parameters, keeping core logic completely decoupled from HTTP adapters or JSON payloads.
104
+
105
+ ```ruby
106
+ class Request
107
+ attr_reader :endpoint, :model, :messages, :prompt, :images, :tools, :schema, :options, :stream
108
+
109
+ def initialize(endpoint:, **args)
110
+ @endpoint = endpoint
111
+ @model = args[:model]
112
+ @messages = args[:messages]
113
+ @prompt = args[:prompt]
114
+ @images = args[:images]
115
+ @tools = args[:tools]
116
+ @schema = args[:schema]
117
+ @options = args[:options] || {}
118
+ @stream = args[:stream]
119
+ freeze
120
+ end
121
+
122
+ def streaming?
123
+ hooks = @options[:hooks]
124
+ (hooks && !hooks.empty?) || @stream == true
125
+ end
126
+ end
127
+ ```
128
+
129
+ ### Response Object
130
+
131
+ Typed accessors wrapping LLM returns instead of raw HTTP hashes:
132
+
133
+ ```ruby
134
+ response.message.content
135
+ response.done?
136
+ response.total_duration
137
+ ```
138
+
139
+ ### Policies Namespace
140
+
141
+ All recovery policies (AutoPull, Retry, RepairJson) live in the `Ollama::Policies` plural namespace for consistency.
@@ -20,6 +20,7 @@ This document addresses three key areas that warrant attention in the `ollama-cl
20
20
  ### Responsibilities
21
21
 
22
22
  The `Client` class currently handles:
23
+
23
24
  1. HTTP communication with Ollama API
24
25
  2. JSON parsing and validation
25
26
  3. Schema validation
@@ -65,12 +66,14 @@ end
65
66
  ```
66
67
 
67
68
  **Benefits:**
69
+
68
70
  - Single Responsibility Principle
69
71
  - Easier to test
70
72
  - Easier to maintain
71
73
  - Can enable RuboCop metrics
72
74
 
73
75
  **Trade-offs:**
76
+
74
77
  - More files to navigate
75
78
  - Slightly more complex initialization
76
79
 
@@ -88,23 +91,27 @@ end
88
91
  ```
89
92
 
90
93
  **Benefits:**
94
+
91
95
  - Still one file
92
96
  - Better organization
93
97
  - Can test modules independently
94
98
 
95
99
  **Trade-offs:**
100
+
96
101
  - Still a large file
97
102
  - RuboCop metrics still problematic
98
103
 
99
104
  #### Option C: Accept Complexity (Current State)
100
105
 
101
106
  **Rationale:**
107
+
102
108
  - Client is the core API surface
103
109
  - All methods are public API
104
110
  - Breaking it up might make usage more complex
105
111
  - Current structure is functional
106
112
 
107
113
  **Action Items:**
114
+
108
115
  - Document why metrics are disabled
109
116
  - Add architectural decision record (ADR)
110
117
  - Consider refactoring in future major version
@@ -122,6 +129,7 @@ end
122
129
  ### Current State
123
130
 
124
131
  **Global Configuration:**
132
+
125
133
  ```ruby
126
134
  module OllamaClient
127
135
  @config_mutex = Mutex.new
@@ -136,6 +144,7 @@ end
136
144
  ```
137
145
 
138
146
  **Issues:**
147
+
139
148
  1. Mutex protects global config access, but warning says "not thread-safe"
140
149
  2. Confusing messaging - mutex IS present but warning suggests it's not safe
141
150
  3. Per-client config is recommended but not enforced
@@ -143,11 +152,13 @@ end
143
152
  ### Analysis
144
153
 
145
154
  **What's Actually Thread-Safe:**
155
+
146
156
  - βœ… Global config access (protected by mutex)
147
157
  - βœ… Per-client instances (each has own config)
148
158
  - βœ… Client methods (stateless, use instance config)
149
159
 
150
160
  **What's NOT Thread-Safe:**
161
+
151
162
  - ⚠️ Modifying global config while clients are using it
152
163
  - ⚠️ Shared config objects between threads (if mutated)
153
164
 
@@ -270,7 +281,7 @@ Need structured output?
270
281
  ```ruby
271
282
  # High-level API for beginners
272
283
  class SimpleAgent
273
- def initialize(model: "llama3.2:3b")
284
+ def initialize(model: "qwen3.5:4b")
274
285
  @client = Ollama::Client.new
275
286
  @model = model
276
287
  end
@@ -294,6 +305,7 @@ end
294
305
  ### Recommended Approach
295
306
 
296
307
  **Immediate:**
308
+
297
309
  1. Add "Quick Start" section to README (Option A)
298
310
  2. Add decision tree diagram (Option B)
299
311
 
data/docs/CLOUD.md CHANGED
@@ -27,3 +27,22 @@ Rules:
27
27
  ## Guardrails
28
28
  - Keep API surface stable and backward compatible.
29
29
  - Update specs when behavior changes.
30
+
31
+ ## Ollama Cloud Model Accessibility
32
+
33
+ Ollama Cloud does not expose a `free` flag per model. The only reliable way to determine whether a model is accessible to your account is to attempt a tiny inference and observe the HTTP response.
34
+
35
+ The project includes a standalone example script that automates this:
36
+
37
+ ```bash
38
+ export OLLAMA_API_KEY="your-ollama-cloud-api-key"
39
+ bundle exec ruby examples/cloud_models.rb
40
+ ```
41
+
42
+ The script:
43
+ 1. Fetches the public cloud model catalog from `https://ollama.com/api/tags`
44
+ 2. Probes each model concurrently (10 threads) with a minimal chat request
45
+ 3. Classifies the response into accessibility statuses (`accessible`, `unauthorized`, `plan_restricted`, `usage_limit`, `unavailable`, `rate_limited`, `timeout`, etc.)
46
+ 4. Prints sorted JSON to stdout
47
+
48
+ See [`examples/cloud_models.rb`](../examples/cloud_models.rb) for the full implementation and [`examples/README.md`](../examples/README.md) for usage details.
@@ -15,6 +15,7 @@ When the LLM was processing a response, the `llm>` prompt appeared immediately,
15
15
  Added a **thinking indicator** that shows while waiting for the API response:
16
16
 
17
17
  **Before:**
18
+
18
19
  ```
19
20
  you> Hi
20
21
  llm> [cursor blinks here - looks like a prompt!]
@@ -23,6 +24,7 @@ llm> [cursor blinks here - looks like a prompt!]
23
24
  ```
24
25
 
25
26
  **After:**
27
+
26
28
  ```
27
29
  you> Hi
28
30
  ... [thinking indicator in cyan]
@@ -62,6 +64,7 @@ end
62
64
  ```
63
65
 
64
66
  **Key Changes:**
67
+
65
68
  - `\r` (carriage return) replaces `...` with `llm>` when first token arrives
66
69
  - `$stdout.flush` ensures immediate visual feedback
67
70
  - Clear visual state: thinking β†’ responding β†’ ready for input
@@ -71,11 +74,13 @@ end
71
74
  ### Problem
72
75
 
73
76
  Tool results were displayed as raw JSON dumps, making it hard to quickly understand:
77
+
74
78
  - **Which tool** was called
75
79
  - **What data** was retrieved
76
80
  - **Key information** from the response
77
81
 
78
82
  **Before:**
83
+
79
84
  ```
80
85
  Tool Results:
81
86
  - get_live_ltp
@@ -103,6 +108,7 @@ Tool Results:
103
108
  Implemented **formatted, human-readable tool results** that extract and display key information:
104
109
 
105
110
  **After:**
111
+
106
112
  ```
107
113
  πŸ”§ Tool Called: get_live_ltp
108
114
  β†’ NIFTY (IDX_I)
@@ -114,6 +120,7 @@ llm> The current price of NIFTY is 25694.35.
114
120
  ### Formatted Output by Tool
115
121
 
116
122
  #### 1. Live LTP (`get_live_ltp`)
123
+
117
124
  ```
118
125
  πŸ”§ Tool Called: get_live_ltp
119
126
  β†’ NIFTY (IDX_I)
@@ -121,6 +128,7 @@ llm> The current price of NIFTY is 25694.35.
121
128
  ```
122
129
 
123
130
  #### 2. Market Quote (`get_market_quote`)
131
+
124
132
  ```
125
133
  πŸ”§ Tool Called: get_market_quote
126
134
  β†’ RELIANCE
@@ -132,6 +140,7 @@ llm> The current price of NIFTY is 25694.35.
132
140
  #### 3. Historical Data (`get_historical_data`)
133
141
 
134
142
  **Regular data:**
143
+
135
144
  ```
136
145
  πŸ”§ Tool Called: get_historical_data
137
146
  β†’ Historical data: 30 records
@@ -139,6 +148,7 @@ llm> The current price of NIFTY is 25694.35.
139
148
  ```
140
149
 
141
150
  **With indicators:**
151
+
142
152
  ```
143
153
  πŸ”§ Tool Called: get_historical_data
144
154
  β†’ Technical Indicators:
@@ -150,6 +160,7 @@ llm> The current price of NIFTY is 25694.35.
150
160
  ```
151
161
 
152
162
  #### 4. Option Chain (`get_option_chain`)
163
+
153
164
  ```
154
165
  πŸ”§ Tool Called: get_option_chain
155
166
  β†’ Spot: β‚Ή25694.35
@@ -158,6 +169,7 @@ llm> The current price of NIFTY is 25694.35.
158
169
  ```
159
170
 
160
171
  #### 5. Expiry List (`get_expiry_list`)
172
+
161
173
  ```
162
174
  πŸ”§ Tool Called: get_expiry_list
163
175
  β†’ Available expiries: 12
@@ -166,6 +178,7 @@ llm> The current price of NIFTY is 25694.35.
166
178
  ```
167
179
 
168
180
  #### 6. Find Instrument (`find_instrument`)
181
+
169
182
  ```
170
183
  πŸ”§ Tool Called: find_instrument
171
184
  β†’ Found: NIFTY
@@ -192,6 +205,7 @@ end
192
205
  ```
193
206
 
194
207
  Each tool has a dedicated formatter that:
208
+
195
209
  1. Extracts key information from the result
196
210
  2. Formats it in a human-readable way
197
211
  3. Uses color coding for better readability
@@ -200,11 +214,13 @@ Each tool has a dedicated formatter that:
200
214
  ## Benefits
201
215
 
202
216
  ### Chat Console
217
+
203
218
  βœ… **Clear visual feedback** - Users know when the LLM is thinking vs responding
204
219
  βœ… **No confusion** - Thinking indicator prevents accidental typing
205
220
  βœ… **Better UX** - Immediate response to user input
206
221
 
207
222
  ### DhanHQ Console
223
+
208
224
  βœ… **Instant comprehension** - See what tool was called at a glance
209
225
  βœ… **Key data highlighted** - Important values (price, volume) are prominent
210
226
  βœ… **Less noise** - No JSON clutter, just the facts
@@ -216,14 +232,16 @@ Each tool has a dedicated formatter that:
216
232
  Both consoles support environment variables:
217
233
 
218
234
  **Chat Console:**
235
+
219
236
  ```bash
220
237
  OLLAMA_BASE_URL=http://localhost:11434
221
- OLLAMA_MODEL=llama3.2:3b
238
+ OLLAMA_MODEL=qwen3.5:4b
222
239
  OLLAMA_TEMPERATURE=0.7
223
240
  OLLAMA_SYSTEM="You are a helpful assistant"
224
241
  ```
225
242
 
226
243
  **DhanHQ Console:**
244
+
227
245
  ```bash
228
246
  # All chat console vars, plus:
229
247
  DHANHQ_CLIENT_ID=your_client_id
@@ -243,6 +261,7 @@ ruby examples/dhan_console.rb
243
261
  ```
244
262
 
245
263
  Try queries like:
264
+
246
265
  - "What is NIFTY price?" β†’ See formatted LTP
247
266
  - "Get RELIANCE quote" β†’ See formatted quote with OHLC
248
267
  - "Show me historical data for NIFTY" β†’ See record count
@@ -0,0 +1,41 @@
1
+ # Ollama Ruby Ecosystem Blueprint
2
+
3
+ This repository remains the deterministic core runtime (`ollama-client`).
4
+
5
+ ## Proposed Gem Split
6
+
7
+ - `ollama-client` (core): transport, retries, schema enforcement, model lifecycle, raw endpoint access, OpenAI facade.
8
+ - `ollama-openai`: standalone OpenAI protocol adapter for external OpenAI SDK consumers (currently provided as an optional extension via `require "ollama/openai"` as an interim step).
9
+ - `ollama-stream`: advanced streaming runtime (SSE object model, resumable streams, WS relay, backpressure primitives).
10
+ - `ollama-observability`: OpenTelemetry spans, metrics, token accounting, structured log emitters.
11
+ - `ollama-agent`: optional orchestration layer (tool runtime, memory, planners) kept separate from core transport.
12
+
13
+ ## Gem Scaffolding Checklist
14
+
15
+ For each new gem:
16
+
17
+ 1. `bundle gem <name> --mit --test=rspec --ci=github`
18
+ 2. Add `README.md` with:
19
+ - scope boundaries
20
+ - compatibility matrix
21
+ - migration guide
22
+ 3. Add `sig/` RBS contracts for public APIs.
23
+ 4. Add integration tests against local Ollama.
24
+ 5. Publish with independent semver and changelog.
25
+
26
+ ## Suggested Release Order
27
+
28
+ 1. `ollama-openai` (highest interoperability impact)
29
+ 2. `ollama-observability`
30
+ 3. `ollama-stream`
31
+ 4. `ollama-agent`
32
+
33
+ ## Non-Goals for Core
34
+
35
+ Core should not include:
36
+
37
+ - vector DB abstractions
38
+ - prompt-chain DSLs
39
+ - opaque β€œmagic” agent workflows
40
+
41
+ Keep core deterministic and infra-grade.