opensearch-sugar 1.0.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.agents/skills/diataxis/SKILL.md +142 -0
  3. data/.agents/skills/diataxis/references/examples.md +420 -0
  4. data/.agents/skills/diataxis/references/explanation-template.md +96 -0
  5. data/.agents/skills/diataxis/references/framework.md +400 -0
  6. data/.agents/skills/diataxis/references/how-to-guide-template.md +105 -0
  7. data/.agents/skills/diataxis/references/reference-template.md +110 -0
  8. data/.agents/skills/diataxis/references/tutorial-template.md +101 -0
  9. data/.agents/skills/diataxis/scripts/generate_index.py +139 -0
  10. data/.rspec +3 -0
  11. data/.standard.yml +3 -0
  12. data/AGENTS.md +120 -0
  13. data/CHANGELOG.md +5 -0
  14. data/Dockerfile.opensearch +4 -0
  15. data/Increase_Coverage.md +311 -0
  16. data/README.md +143 -0
  17. data/Rakefile +27 -0
  18. data/Steepfile +23 -0
  19. data/adrs/ADR-000-template.md +87 -0
  20. data/adrs/ADR-001-simpledelegator-for-client.md +138 -0
  21. data/adrs/ADR-002-facade-pattern-for-index.md +126 -0
  22. data/adrs/ADR-003-repository-pattern-for-models.md +148 -0
  23. data/adrs/ADR-004-integration-tests-no-mocking.md +91 -0
  24. data/adrs/ADR-005-exceptions-over-result-objects.md +107 -0
  25. data/adrs/ADR-006-ssl-on-by-default.md +95 -0
  26. data/adrs/ADR-007-selective-sugar-surface.md +118 -0
  27. data/adrs/ADR-008-integration-test-design.md +178 -0
  28. data/compose.yml +2 -0
  29. data/compose_opensearch.yml +31 -0
  30. data/docs/HOWTO.md +844 -0
  31. data/docs/REFERENCE.md +725 -0
  32. data/docs/TUTORIAL.md +327 -0
  33. data/docs/alias-api-design-notes.md +119 -0
  34. data/lib/opensearch/sugar/client.rb +300 -0
  35. data/lib/opensearch/sugar/index/include/utilities.rb +6 -0
  36. data/lib/opensearch/sugar/index.rb +339 -0
  37. data/lib/opensearch/sugar/models.rb +209 -0
  38. data/lib/opensearch/sugar/version.rb +8 -0
  39. data/lib/opensearch/sugar.rb +61 -0
  40. data/old_docs/DELEGATED_METHODS_ANALYSIS.md +361 -0
  41. data/old_docs/EXPLANATION.md +685 -0
  42. data/old_docs/README.md +155 -0
  43. data/old_docs/docs/CLI-PROPOSAL.md +257 -0
  44. data/old_docs/docs/HOWTO.md +798 -0
  45. data/old_docs/docs/REFERENCE.md +901 -0
  46. data/old_docs/docs/TUTORIAL.md +493 -0
  47. data/sig/opensearch/sugar.rbs +162 -0
  48. metadata +240 -0
@@ -0,0 +1,493 @@
1
+ # Getting Started with OpenSearch::Sugar
2
+
3
+ *(Documentation written by GitHub Copilot, powered by Claude Sonnet 4.5)*
4
+
5
+ This tutorial will guide you through building your first OpenSearch application using OpenSearch::Sugar. By the end, you'll have created a searchable book catalog with custom analyzers and full-text search capabilities.
6
+
7
+ ## What You'll Learn
8
+
9
+ - How to connect to OpenSearch
10
+ - How to create and configure an index
11
+ - How to define custom analyzers
12
+ - How to add and query documents
13
+ - How to use text analysis features
14
+
15
+ ## Prerequisites
16
+
17
+ - Ruby 3.1 or higher installed
18
+ - Docker (for running OpenSearch locally)
19
+ - Basic Ruby knowledge
20
+ - Familiarity with command line
21
+
22
+ ## Step 1: Set Up Your Environment
23
+
24
+ ### Install OpenSearch
25
+
26
+ First, let's start an OpenSearch instance using Docker:
27
+
28
+ ```bash
29
+ docker run -d \
30
+ -p 9200:9200 \
31
+ -p 9600:9600 \
32
+ -e "discovery.type=single-node" \
33
+ -e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=MyPassword123!" \
34
+ --name opensearch-tutorial \
35
+ opensearchproject/opensearch:latest
36
+ ```
37
+
38
+ Wait a few seconds for OpenSearch to start, then verify it's running:
39
+
40
+ ```bash
41
+ curl -X GET "https://localhost:9200" -ku admin:MyPassword123!
42
+ ```
43
+
44
+ You should see a JSON response with cluster information.
45
+
46
+ ### Install the Gem
47
+
48
+ Create a new Ruby project:
49
+
50
+ ```bash
51
+ mkdir book-catalog
52
+ cd book-catalog
53
+ ```
54
+
55
+ Create a `Gemfile`:
56
+
57
+ ```ruby
58
+ source 'https://rubygems.org'
59
+
60
+ gem 'opensearch-sugar'
61
+ gem 'dotenv' # For managing environment variables
62
+ ```
63
+
64
+ Install dependencies:
65
+
66
+ ```bash
67
+ bundle install
68
+ ```
69
+
70
+ ### Configure Connection
71
+
72
+ Create a `.env` file with your OpenSearch connection details:
73
+
74
+ ```bash
75
+ OPENSEARCH_URL=https://localhost:9200
76
+ OPENSEARCH_USER=admin
77
+ OPENSEARCH_PASSWORD=MyPassword123!
78
+ ```
79
+
80
+ ## Step 2: Connect to OpenSearch
81
+
82
+ Create a file called `catalog.rb` and add:
83
+
84
+ ```ruby
85
+ require 'opensearch/sugar'
86
+ require 'dotenv/load'
87
+
88
+ # Create a client - it will automatically use environment variables
89
+ client = OpenSearch::Sugar.new
90
+
91
+ puts "Connected to OpenSearch!"
92
+ puts "Available indexes: #{client.index_names.join(', ')}"
93
+ ```
94
+
95
+ Run it:
96
+
97
+ ```bash
98
+ ruby catalog.rb
99
+ ```
100
+
101
+ You should see a confirmation that you're connected!
102
+
103
+ ## Step 3: Create Your First Index
104
+
105
+ Let's create an index for storing books. Add to your `catalog.rb`:
106
+
107
+ ```ruby
108
+ # Create or open the books index
109
+ index = client.open_or_create_index('books')
110
+
111
+ puts "Index 'books' is ready!"
112
+ puts "Document count: #{index.count}"
113
+ ```
114
+
115
+ Run it again. The index is now created!
116
+
117
+ ## Step 4: Configure Custom Analyzers
118
+
119
+ Books need good text analysis for searching. Let's add a custom analyzer that handles book titles and descriptions well.
120
+
121
+ Add this to your script:
122
+
123
+ ```ruby
124
+ # Define custom analyzers for book text
125
+ settings = {
126
+ settings: {
127
+ analysis: {
128
+ # Custom analyzers
129
+ analyzer: {
130
+ book_title_analyzer: {
131
+ type: 'custom',
132
+ tokenizer: 'standard',
133
+ filter: ['lowercase', 'asciifolding', 'book_stop_filter']
134
+ },
135
+ book_search_analyzer: {
136
+ type: 'custom',
137
+ tokenizer: 'standard',
138
+ filter: ['lowercase', 'asciifolding']
139
+ }
140
+ },
141
+ # Custom stop words filter
142
+ filter: {
143
+ book_stop_filter: {
144
+ type: 'stop',
145
+ stopwords: ['the', 'a', 'an', 'and', 'or', 'but']
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ index.update_settings(settings)
153
+ puts "Settings updated!"
154
+ ```
155
+
156
+ **What's happening here?**
157
+ - `book_title_analyzer` removes common words like "the" and "a"
158
+ - `book_search_analyzer` keeps all words for broader searching
159
+ - Both handle special characters with `asciifolding`
160
+
161
+ ## Step 5: Define Field Mappings
162
+
163
+ Now let's define the structure of our book documents:
164
+
165
+ ```ruby
166
+ mappings = {
167
+ mappings: {
168
+ properties: {
169
+ title: {
170
+ type: 'text',
171
+ analyzer: 'book_title_analyzer',
172
+ search_analyzer: 'book_search_analyzer',
173
+ fields: {
174
+ keyword: { type: 'keyword' } # For exact matching
175
+ }
176
+ },
177
+ author: {
178
+ type: 'text',
179
+ fields: {
180
+ keyword: { type: 'keyword' }
181
+ }
182
+ },
183
+ description: {
184
+ type: 'text',
185
+ analyzer: 'book_title_analyzer'
186
+ },
187
+ isbn: {
188
+ type: 'keyword'
189
+ },
190
+ published_date: {
191
+ type: 'date'
192
+ },
193
+ pages: {
194
+ type: 'integer'
195
+ },
196
+ categories: {
197
+ type: 'keyword'
198
+ },
199
+ rating: {
200
+ type: 'float'
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ index.update_mappings(mappings)
207
+ puts "Mappings updated!"
208
+ ```
209
+
210
+ ## Step 6: Test Your Analyzer
211
+
212
+ Before adding documents, let's verify our analyzer works correctly:
213
+
214
+ ```ruby
215
+ # Test the analyzer
216
+ sample_title = "The Lord of the Rings: The Fellowship of the Ring"
217
+ tokens = index.analyze_text(
218
+ analyzer: 'book_title_analyzer',
219
+ text: sample_title
220
+ )
221
+
222
+ puts "\nOriginal: #{sample_title}"
223
+ puts "Tokens: #{tokens.join(', ')}"
224
+ # Output: lord, rings, fellowship, ring
225
+ # Notice "the" and "of" are removed!
226
+ ```
227
+
228
+ This shows how OpenSearch will index book titles - common words are removed to improve search relevance.
229
+
230
+ ## Step 7: Add Some Books
231
+
232
+ Now let's add books using the raw client (OpenSearch::Sugar delegates all standard client methods):
233
+
234
+ ```ruby
235
+ # Add some books
236
+ books = [
237
+ {
238
+ title: "The Hobbit",
239
+ author: "J.R.R. Tolkien",
240
+ description: "A fantasy adventure about a hobbit's unexpected journey.",
241
+ isbn: "978-0547928227",
242
+ published_date: "1937-09-21",
243
+ pages: 310,
244
+ categories: ["fantasy", "adventure", "classic"],
245
+ rating: 4.7
246
+ },
247
+ {
248
+ title: "1984",
249
+ author: "George Orwell",
250
+ description: "A dystopian novel about totalitarianism and surveillance.",
251
+ isbn: "978-0451524935",
252
+ published_date: "1949-06-08",
253
+ pages: 328,
254
+ categories: ["dystopian", "political", "classic"],
255
+ rating: 4.6
256
+ },
257
+ {
258
+ title: "To Kill a Mockingbird",
259
+ author: "Harper Lee",
260
+ description: "A novel about racial injustice in the American South.",
261
+ isbn: "978-0061120084",
262
+ published_date: "1960-07-11",
263
+ pages: 324,
264
+ categories: ["classic", "legal", "drama"],
265
+ rating: 4.8
266
+ }
267
+ ]
268
+
269
+ books.each do |book|
270
+ # Use the raw client for indexing
271
+ client.index(
272
+ index: 'books',
273
+ id: book[:isbn],
274
+ body: book
275
+ )
276
+ end
277
+
278
+ # Refresh to make documents searchable immediately
279
+ index.refresh
280
+
281
+ puts "\nAdded #{books.size} books!"
282
+ puts "Total documents: #{index.count}"
283
+ ```
284
+
285
+ ## Step 8: Search Your Catalog
286
+
287
+ Now let's search! Add this to test searching:
288
+
289
+ ```ruby
290
+ # Search for books
291
+ search_response = client.search(
292
+ index: 'books',
293
+ body: {
294
+ query: {
295
+ multi_match: {
296
+ query: 'fantasy adventure',
297
+ fields: ['title^2', 'description', 'categories']
298
+ }
299
+ }
300
+ }
301
+ )
302
+
303
+ puts "\nSearch results for 'fantasy adventure':"
304
+ search_response['hits']['hits'].each do |hit|
305
+ book = hit['_source']
306
+ puts " - #{book['title']} by #{book['author']} (score: #{hit['_score']})"
307
+ end
308
+ ```
309
+
310
+ ## Step 9: Analyze Search Behavior
311
+
312
+ Let's see how our analyzer affects search results:
313
+
314
+ ```ruby
315
+ # Compare analyzers
316
+ title = "The Complete Adventures of Sherlock Holmes"
317
+
318
+ puts "\nAnalyzing: #{title}"
319
+
320
+ # With stop words removed
321
+ tokens_indexed = index.analyze_text(
322
+ analyzer: 'book_title_analyzer',
323
+ text: title
324
+ )
325
+ puts "Indexed tokens (with stop filter): #{tokens_indexed.join(', ')}"
326
+
327
+ # Without stop words removed
328
+ tokens_search = index.analyze_text(
329
+ analyzer: 'book_search_analyzer',
330
+ text: title
331
+ )
332
+ puts "Search tokens (without stop filter): #{tokens_search.join(', ')}"
333
+ ```
334
+
335
+ ## Step 10: Create an Alias
336
+
337
+ Create an alias for easy access:
338
+
339
+ ```ruby
340
+ # Create an alias
341
+ index.create_alias('current_catalog')
342
+
343
+ puts "\nAliases for 'books': #{index.aliases.join(', ')}"
344
+
345
+ # Now you can use the alias
346
+ alias_index = client['current_catalog']
347
+ puts "Documents via alias: #{alias_index.count}"
348
+ ```
349
+
350
+ ## What You've Accomplished
351
+
352
+ Congratulations! You've built a complete searchable book catalog:
353
+
354
+ - ✅ Connected to OpenSearch
355
+ - ✅ Created a custom index with analyzers
356
+ - ✅ Defined field mappings for structured data
357
+ - ✅ Added documents with proper indexing
358
+ - ✅ Performed full-text searches
359
+ - ✅ Analyzed how text processing works
360
+ - ✅ Created index aliases
361
+
362
+ ## Complete Script
363
+
364
+ Here's the complete `catalog.rb` for reference:
365
+
366
+ ```ruby
367
+ require 'opensearch/sugar'
368
+ require 'dotenv/load'
369
+
370
+ # Connect
371
+ client = OpenSearch::Sugar.new
372
+ index = client.open_or_create_index('books')
373
+
374
+ # Configure settings
375
+ settings = {
376
+ settings: {
377
+ analysis: {
378
+ analyzer: {
379
+ book_title_analyzer: {
380
+ type: 'custom',
381
+ tokenizer: 'standard',
382
+ filter: ['lowercase', 'asciifolding', 'book_stop_filter']
383
+ },
384
+ book_search_analyzer: {
385
+ type: 'custom',
386
+ tokenizer: 'standard',
387
+ filter: ['lowercase', 'asciifolding']
388
+ }
389
+ },
390
+ filter: {
391
+ book_stop_filter: {
392
+ type: 'stop',
393
+ stopwords: ['the', 'a', 'an', 'and', 'or', 'but']
394
+ }
395
+ }
396
+ }
397
+ }
398
+ }
399
+ index.update_settings(settings)
400
+
401
+ # Configure mappings
402
+ mappings = {
403
+ mappings: {
404
+ properties: {
405
+ title: {
406
+ type: 'text',
407
+ analyzer: 'book_title_analyzer',
408
+ search_analyzer: 'book_search_analyzer',
409
+ fields: { keyword: { type: 'keyword' } }
410
+ },
411
+ author: { type: 'text', fields: { keyword: { type: 'keyword' } } },
412
+ description: { type: 'text', analyzer: 'book_title_analyzer' },
413
+ isbn: { type: 'keyword' },
414
+ published_date: { type: 'date' },
415
+ pages: { type: 'integer' },
416
+ categories: { type: 'keyword' },
417
+ rating: { type: 'float' }
418
+ }
419
+ }
420
+ }
421
+ index.update_mappings(mappings)
422
+
423
+ # Add books
424
+ books = [
425
+ {
426
+ title: "The Hobbit",
427
+ author: "J.R.R. Tolkien",
428
+ description: "A fantasy adventure about a hobbit's unexpected journey.",
429
+ isbn: "978-0547928227",
430
+ published_date: "1937-09-21",
431
+ pages: 310,
432
+ categories: ["fantasy", "adventure", "classic"],
433
+ rating: 4.7
434
+ }
435
+ # ... add more books
436
+ ]
437
+
438
+ books.each do |book|
439
+ client.index(index: 'books', id: book[:isbn], body: book)
440
+ end
441
+
442
+ index.refresh
443
+ puts "Created catalog with #{index.count} books!"
444
+ ```
445
+
446
+ ## Next Steps
447
+
448
+ Now that you understand the basics, explore:
449
+
450
+ - **[How-to Guides](HOWTO.md)** - Solve specific problems
451
+ - **[Reference Documentation](REFERENCE.md)** - Complete API details
452
+ - **[Explanation](EXPLANATION.md)** - Understand design decisions
453
+ - **[OpenSearch Documentation](https://opensearch.org/docs/latest/)** - Deep dive into OpenSearch features
454
+
455
+ ## Cleanup
456
+
457
+ When you're done experimenting:
458
+
459
+ ```ruby
460
+ # Delete the index
461
+ index.delete!
462
+
463
+ # Stop the Docker container
464
+ # docker stop opensearch-tutorial
465
+ # docker rm opensearch-tutorial
466
+ ```
467
+
468
+ ## Troubleshooting
469
+
470
+ **Connection refused?**
471
+ - Make sure OpenSearch is running: `docker ps`
472
+ - Check the URL in your `.env` file
473
+
474
+ **SSL certificate errors?**
475
+ - The default settings disable SSL verification for development
476
+ - For production, enable proper SSL verification
477
+
478
+ **Index already exists?**
479
+ - Delete it first: `client.indices.delete(index: 'books')`
480
+ - Or use `open_or_create_index` instead of `create`
481
+
482
+ **Settings update fails?**
483
+ - Some settings can't be changed on an open index
484
+ - The gem automatically closes/reopens the index for you
485
+ - If it fails, check the error message
486
+
487
+ ## Resources
488
+
489
+ - [OpenSearch Index Settings](https://opensearch.org/docs/latest/install-and-configure/configuring-opensearch/index-settings/)
490
+ - [OpenSearch Analyzers](https://opensearch.org/docs/latest/analyzers/)
491
+ - [OpenSearch Mapping](https://opensearch.org/docs/latest/field-types/)
492
+ - [OpenSearch Query DSL](https://opensearch.org/docs/latest/query-dsl/)
493
+
@@ -0,0 +1,162 @@
1
+ # RBS type signatures for the OpenSearch::Sugar public API.
2
+ # Keep in sync with lib/opensearch/sugar/**/*.rb.
3
+
4
+ module OpenSearch
5
+ module Sugar
6
+ VERSION: String
7
+
8
+ class Error < StandardError
9
+ end
10
+
11
+ # Convenience factory — returns a new Client.
12
+ def self.client: (**untyped kwargs) -> Client
13
+
14
+ # Alias for .client.
15
+ def self.new: (**untyped kwargs) -> Client
16
+
17
+ class Client < ::SimpleDelegator
18
+ attr_reader raw_client: untyped
19
+ attr_reader models: Models
20
+
21
+ def self.raw_client: (*untyped args, **untyped kwargs) -> untyped
22
+
23
+ def initialize: (?host: String, **untyped kwargs) -> void
24
+
25
+ def default_args: () -> Hash[Symbol, untyped]
26
+
27
+ # Returns true if the named index exists.
28
+ def has_index?: (String name) -> bool
29
+
30
+ # Returns the names of all indices in the cluster.
31
+ def index_names: () -> Array[String]
32
+
33
+ # Opens the named index and returns an Index object.
34
+ # Raises ArgumentError if the index does not exist.
35
+ def []: (String index_name) -> Index
36
+
37
+ # Opens the named index if it exists; creates it otherwise.
38
+ def open_or_create_index: (String index_name) -> Index
39
+
40
+ # Deletes the named index.
41
+ # Raises if the index does not exist.
42
+ def delete_index!: (String index_name) -> untyped
43
+
44
+ # Applies settings to an index (close → put_settings → open).
45
+ # Raises OpenSearch::Sugar::Error on failure.
46
+ def update_settings: (Hash[untyped, untyped] settings, String index_name) -> untyped
47
+
48
+ # Applies mappings to an index (close → put_mapping → open).
49
+ # Raises OpenSearch::Sugar::Error on failure.
50
+ def update_mappings: (Hash[untyped, untyped] mappings, String index_name) -> untyped
51
+
52
+ # Sets a cluster-level log level.
53
+ def set_log_level: (?logger: String, ?level: String) -> untyped
54
+
55
+ # Tests a custom transient analyzer defined inline by components, without
56
+ # requiring the analyzer to be registered on any index.
57
+ def test_analyzer_by_definition: (text: String, tokenizer: String, ?filter: Array[String | Hash[untyped, untyped]], ?char_filter: Array[String | Hash[untyped, untyped]]) -> Array[String | Array[String]]
58
+
59
+ private
60
+
61
+ def reopen_index: (String index_name) -> void
62
+ end
63
+
64
+ class Index
65
+ attr_accessor client: Client
66
+ attr_accessor name: String
67
+
68
+ module Include
69
+ module Utilities
70
+ end
71
+ end
72
+
73
+ # Opens an existing index. Raises ArgumentError if not found.
74
+ def self.open: (client: Client, name: String) -> Index
75
+
76
+ # Creates a new index. Raises ArgumentError if it already exists.
77
+ def self.create: (client: Client, name: String, ?knn: bool) -> Index
78
+
79
+ def settings: () -> Hash[String, untyped]
80
+ def update_settings: (Hash[untyped, untyped] settings) -> untyped
81
+
82
+ def mappings: () -> Hash[String, untyped]
83
+ def update_mappings: (Hash[untyped, untyped] mappings) -> untyped
84
+
85
+ def delete!: () -> untyped
86
+
87
+ # Forces an index refresh so recently indexed documents are immediately searchable.
88
+ def refresh: () -> untyped
89
+
90
+ def count: () -> Integer
91
+
92
+ def aliases: () -> Array[String]
93
+ def create_alias: (String alias_name) -> Array[String]
94
+
95
+ def all_available_analyzers: () -> Array[String]
96
+ def analyzers: () -> Array[String]
97
+
98
+ # Returns tokens produced by the named analyzer (must be registered on this index).
99
+ # Each element is a String token, or an Array[String] for same-position synonyms.
100
+ def test_analyzer_by_name: (analyzer: String, text: String) -> Array[String | Array[String]]
101
+
102
+ # Deprecated alias for test_analyzer_by_name.
103
+ def analyze_text: (analyzer: String, text: String) -> Array[String | Array[String]]
104
+
105
+ # Same as test_analyzer_by_name but derives the analyzer from the field mapping.
106
+ def test_analyzer_by_fieldname: (field: String, text: String) -> Array[String | Array[String]]
107
+
108
+ # Deprecated alias for test_analyzer_by_fieldname.
109
+ def analyze_text_field: (field: String, text: String) -> Array[String | Array[String]]
110
+
111
+ def delete_by_id: (String id) -> untyped
112
+ def clear!: () -> Integer
113
+
114
+ def index_document: (Hash[untyped, untyped] doc, String id) -> untyped
115
+
116
+ # Indexes all documents from a JSONL file path or IO-like object.
117
+ def index_jsonl_file: (String | _EachLine source, id_field: Symbol | String) -> void
118
+
119
+ interface _EachLine
120
+ def each_line: () { (String) -> void } -> void
121
+ end
122
+
123
+ private
124
+
125
+ def initialize: (client: Client, name: String) -> void
126
+ def default_index_body: () -> Hash[Symbol, untyped]
127
+ end
128
+
129
+ class Models
130
+ # Struct holding name, version, and id for a deployed ML model.
131
+ class ML_INFO
132
+ attr_reader name: String
133
+ attr_reader version: String
134
+ attr_reader id: String
135
+
136
+ def initialize: (String name, String version, String id) -> void
137
+ end
138
+
139
+ def initialize: (Client os) -> void
140
+
141
+ # Registers and deploys an ML model. Idempotent — returns existing if found.
142
+ def register: (name: String, version: String, ?format: String) -> ML_INFO?
143
+
144
+ alias deploy register
145
+
146
+ # Looks up a model by exact name, exact id, or case-insensitive partial name.
147
+ def []: (String id_or_fullname_or_nickname) -> ML_INFO?
148
+
149
+ def list: () -> Array[ML_INFO]
150
+ def raw_list: () -> untyped
151
+
152
+ def undeploy!: (String name_or_id) -> untyped
153
+ def delete!: (String name_or_id) -> untyped
154
+
155
+ # Creates an ingest pipeline for text-embedding using the named model.
156
+ def create_pipeline: (name: String, model: String, description: String, field_map: Hash[String, String]) -> untyped
157
+
158
+ # Deletes an ingest pipeline by name.
159
+ def delete_pipeline!: (String pipeline_name) -> untyped
160
+ end
161
+ end
162
+ end