multiwoven-integrations 0.40.1 → 0.41.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/lib/multiwoven/integrations/config.rb +4 -1
- data/lib/multiwoven/integrations/core/base_connector.rb +33 -6
- data/lib/multiwoven/integrations/core/constants.rb +1 -0
- data/lib/multiwoven/integrations/core/open_router_catalog.rb +212 -0
- data/lib/multiwoven/integrations/protocol/protocol.json +54 -0
- data/lib/multiwoven/integrations/protocol/protocol.rb +39 -1
- data/lib/multiwoven/integrations/rollout.rb +3 -1
- data/lib/multiwoven/integrations/service.rb +17 -8
- data/lib/multiwoven/integrations/source/aisquared/config/meta.json +8 -2
- data/lib/multiwoven/integrations/source/aisquared/config/models.json +120 -0
- data/lib/multiwoven/integrations/source/aisquared/config/spec.json +6 -1
- data/lib/multiwoven/integrations/source/anthropic/config/meta.json +9 -2
- data/lib/multiwoven/integrations/source/anthropic/config/models.json +4 -0
- data/lib/multiwoven/integrations/source/anthropic/config/spec.json +7 -1
- data/lib/multiwoven/integrations/source/aws_bedrock_model/config/meta.json +8 -2
- data/lib/multiwoven/integrations/source/aws_bedrock_model/config/models.json +96 -0
- data/lib/multiwoven/integrations/source/aws_sagemaker_model/config/meta.json +8 -2
- data/lib/multiwoven/integrations/source/databrics_model/config/meta.json +20 -15
- data/lib/multiwoven/integrations/source/generic_open_ai/client.rb +15 -8
- data/lib/multiwoven/integrations/source/generic_open_ai/config/meta.json +8 -1
- data/lib/multiwoven/integrations/source/google_gemini/client.rb +18 -0
- data/lib/multiwoven/integrations/source/google_gemini/config/catalog.json +6 -0
- data/lib/multiwoven/integrations/source/google_gemini/config/meta.json +23 -0
- data/lib/multiwoven/integrations/source/google_gemini/config/models.json +26 -0
- data/lib/multiwoven/integrations/source/google_gemini/config/spec.json +71 -0
- data/lib/multiwoven/integrations/source/google_gemini/icon.svg +10 -0
- data/lib/multiwoven/integrations/source/google_vertex_model/config/meta.json +7 -2
- data/lib/multiwoven/integrations/source/http_model/config/meta.json +7 -1
- data/lib/multiwoven/integrations/source/john_snow_labs/client.rb +18 -0
- data/lib/multiwoven/integrations/source/john_snow_labs/config/catalog.json +6 -0
- data/lib/multiwoven/integrations/source/john_snow_labs/config/meta.json +24 -0
- data/lib/multiwoven/integrations/source/john_snow_labs/config/models.json +119 -0
- data/lib/multiwoven/integrations/source/john_snow_labs/config/spec.json +71 -0
- data/lib/multiwoven/integrations/source/john_snow_labs/icon.svg +16 -0
- data/lib/multiwoven/integrations/source/open_ai/config/meta.json +9 -2
- data/lib/multiwoven/integrations/source/open_ai/config/models.json +26 -0
- data/lib/multiwoven/integrations/source/open_ai/config/spec.json +7 -1
- data/lib/multiwoven/integrations/source/watsonx_ai/config/meta.json +8 -2
- data/lib/multiwoven/integrations/source/watsonx_ai/config/models.json +73 -0
- data/lib/multiwoven/integrations.rb +4 -0
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ad58beae1f92f76c184383a3e8c1741ee51356e6f3692bfae35ec73be5f9e351
|
|
4
|
+
data.tar.gz: 3b8e0bd9360c53a532157172c2e70d0a38d53a3c8aa9f26d11af35b99082f912
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 317c2605f975ec6726413bb1d26cb8716f5c4150c515bb666f26ec3a7265f4a905eddef60d739effc07d492dc9238bb2c1ecef9358118921b3e88e52a90f58c9
|
|
7
|
+
data.tar.gz: ca2256921a2839449fee7e9a97d3c76a6ea54474df87f67124c8f5e31acc58df4077f34c7762e0525c66ea2fcc2536adcb21887eea41f0b0d7c949d7bfc479eb
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
module Multiwoven
|
|
4
4
|
module Integrations
|
|
5
5
|
class Config
|
|
6
|
-
|
|
6
|
+
# cache: optional host store responding to read(key), write(key, value,
|
|
7
|
+
# expires_in:) and delete(key). Without one, caching is a per-process memo.
|
|
8
|
+
attr_accessor :logger, :exception_reporter, :cache
|
|
7
9
|
|
|
8
10
|
def initialize(params = {})
|
|
9
11
|
@logger = params[:logger]
|
|
10
12
|
@exception_reporter = params[:exception_reporter]
|
|
13
|
+
@cache = params[:cache]
|
|
11
14
|
end
|
|
12
15
|
end
|
|
13
16
|
end
|
|
@@ -8,20 +8,47 @@ module Multiwoven
|
|
|
8
8
|
include Constants
|
|
9
9
|
|
|
10
10
|
def connector_spec
|
|
11
|
-
@connector_spec ||=
|
|
12
|
-
spec_json = keys_to_symbols(read_json(CONNECTOR_SPEC_PATH)).to_json
|
|
13
|
-
# returns Protocol::ConnectorSpecification
|
|
14
|
-
ConnectorSpecification.from_json(spec_json)
|
|
15
|
-
end
|
|
11
|
+
@connector_spec ||= ConnectorSpecification.from_json(keys_to_symbols(read_json(CONNECTOR_SPEC_PATH)).to_json)
|
|
16
12
|
end
|
|
17
13
|
|
|
18
14
|
def meta_data
|
|
15
|
+
return @meta_data if @meta_data
|
|
16
|
+
|
|
19
17
|
client_meta_data = read_json(META_DATA_PATH).deep_symbolize_keys
|
|
20
18
|
icon_name = client_meta_data[:data][:icon]
|
|
21
19
|
icon_url = "https://raw.githubusercontent.com/Multiwoven/multiwoven/main/integrations#{relative_path}/#{icon_name}"
|
|
22
20
|
client_meta_data[:data][:icon] = icon_url
|
|
23
21
|
# returns hash
|
|
24
|
-
@meta_data
|
|
22
|
+
@meta_data = client_meta_data
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def model_catalog
|
|
26
|
+
@model_catalog ||= ModelCatalog.new(models: (live_models + curated_models).uniq { |model| model[:id] })
|
|
27
|
+
rescue Dry::Struct::Error => e
|
|
28
|
+
Integrations::Service.logger.error("#{self.class}: unusable model catalog: #{e.message}")
|
|
29
|
+
@model_catalog = ModelCatalog.new(models: [])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Symbolized to match models from upstream; read_json hands back string keys,
|
|
33
|
+
# which leaves callers reading nil.
|
|
34
|
+
def curated_models
|
|
35
|
+
@curated_models ||= Array(read_json(MODELS_SPEC_PATH)["models"]).map(&:deep_symbolize_keys)
|
|
36
|
+
rescue Errno::ENOENT
|
|
37
|
+
@curated_models = []
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
Integrations::Service.logger.error("#{self.class}: unusable models.json: #{e.message}")
|
|
40
|
+
@curated_models = []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def live_models
|
|
44
|
+
slug = meta_data[:data][:openrouter_slug]
|
|
45
|
+
return [] if slug.blank?
|
|
46
|
+
|
|
47
|
+
overrides = meta_data[:data][:model_id_overrides] || {}
|
|
48
|
+
Integrations::Core::OpenRouterCatalog.fetch.models_for(slug).map do |model|
|
|
49
|
+
pinned = overrides[model[:id].to_sym] || overrides[model[:id].to_s]
|
|
50
|
+
pinned.present? ? model.merge(id: pinned) : model
|
|
51
|
+
end
|
|
25
52
|
end
|
|
26
53
|
|
|
27
54
|
def relative_path
|
|
@@ -7,6 +7,7 @@ module Multiwoven
|
|
|
7
7
|
META_DATA_PATH = "config/meta.json"
|
|
8
8
|
CONNECTOR_SPEC_PATH = "config/spec.json"
|
|
9
9
|
CATALOG_SPEC_PATH = "config/catalog.json"
|
|
10
|
+
MODELS_SPEC_PATH = "config/models.json"
|
|
10
11
|
SNOWFLAKE_MAC_DRIVER_PATH = "/opt/snowflake/snowflakeodbc/lib/universal/libSnowflake.dylib"
|
|
11
12
|
DATABRICKS_MAC_DRIVER_PATH = "/Library/simba/spark/lib/libsparkodbc_sb64-universal.dylib"
|
|
12
13
|
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Multiwoven
|
|
4
|
+
module Integrations::Core
|
|
5
|
+
# Live model metadata from OpenRouter's public /models endpoint, so a connector
|
|
6
|
+
# need not ship prices that go stale. Providers opt in with an openrouter_slug;
|
|
7
|
+
# embeddings and partner-hosted models stay curated in models.json.
|
|
8
|
+
class OpenRouterCatalog
|
|
9
|
+
MODELS_URL = "https://openrouter.ai/api/v1/models"
|
|
10
|
+
CACHE_KEY = "multiwoven_open_router_catalog_v1"
|
|
11
|
+
CACHE_TTL = 86_400
|
|
12
|
+
REQUEST_CONFIG = { timeout: 10, open_timeout: 5 }.freeze
|
|
13
|
+
PER_MILLION = 1_000_000
|
|
14
|
+
|
|
15
|
+
Result = Struct.new(:models_by_slug, :fetched_at, :source, keyword_init: true) do
|
|
16
|
+
def models_for(slug)
|
|
17
|
+
return [] if slug.blank?
|
|
18
|
+
|
|
19
|
+
models_by_slug.fetch(slug.to_s.downcase, [])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
# Two layers: a host cache may be absent or a null store, so the memo is
|
|
25
|
+
# what prevents a re-fetch of half a megabyte per request.
|
|
26
|
+
def fetch
|
|
27
|
+
return @memo if live_memo?
|
|
28
|
+
|
|
29
|
+
result = to_result(cached_payload)
|
|
30
|
+
@memo = result if result.source == "live"
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def reset!
|
|
35
|
+
@memo = nil
|
|
36
|
+
cache&.delete(CACHE_KEY)
|
|
37
|
+
rescue StandardError => e
|
|
38
|
+
log_cache_failure("delete", e)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def cache
|
|
44
|
+
Integrations::Service.config.cache
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def live_memo?
|
|
48
|
+
@memo&.source == "live" && @memo.fetched_at && @memo.fetched_at > Time.now - CACHE_TTL
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def cached_payload
|
|
52
|
+
store = cache
|
|
53
|
+
return build_payload if store.nil?
|
|
54
|
+
|
|
55
|
+
cached = read_cache(store)
|
|
56
|
+
return cached if live_payload?(cached)
|
|
57
|
+
|
|
58
|
+
payload = build_payload
|
|
59
|
+
write_cache(store, payload) if live_payload?(payload)
|
|
60
|
+
payload
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# A failing host cache degrades to a direct fetch rather than taking the
|
|
64
|
+
# whole catalog down with it.
|
|
65
|
+
def read_cache(store)
|
|
66
|
+
store.read(CACHE_KEY)
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
log_cache_failure("read", e)
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def write_cache(store, payload)
|
|
73
|
+
store.write(CACHE_KEY, payload, expires_in: CACHE_TTL)
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
log_cache_failure("write", e)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def log_cache_failure(operation, error)
|
|
79
|
+
Integrations::Service.logger.error(
|
|
80
|
+
"OpenRouterCatalog: cache #{operation} failed: #{error.class}: #{error.message}"
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def live_payload?(payload)
|
|
85
|
+
payload.present? && payload.with_indifferent_access[:source] == "live"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Slugs stay strings because models_for looks them up that way; the model
|
|
89
|
+
# hashes are symbolized because every reader indexes them with symbols.
|
|
90
|
+
def symbolize_models(by_slug)
|
|
91
|
+
(by_slug || {}).each_with_object({}) do |(slug, models), acc|
|
|
92
|
+
acc[slug.to_s.downcase] = Array(models).map(&:deep_symbolize_keys)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def to_result(payload)
|
|
97
|
+
payload = payload.transform_keys(&:to_sym)
|
|
98
|
+
Result.new(
|
|
99
|
+
models_by_slug: symbolize_models(payload[:models_by_slug]),
|
|
100
|
+
fetched_at: payload[:fetched_at],
|
|
101
|
+
source: payload[:source]
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def build_payload
|
|
106
|
+
response = Integrations::Core::HttpClient.request(
|
|
107
|
+
MODELS_URL, "GET", options: { config: REQUEST_CONFIG }
|
|
108
|
+
)
|
|
109
|
+
body = JSON.parse(response.body)
|
|
110
|
+
raise "OpenRouter returned no data" unless body["data"].is_a?(Array)
|
|
111
|
+
|
|
112
|
+
{ models_by_slug: group_by_slug(body["data"]), fetched_at: Time.now, source: "live" }
|
|
113
|
+
rescue StandardError => e
|
|
114
|
+
# Fail soft: callers still see curated models rather than an exception.
|
|
115
|
+
Integrations::Service.logger.error("OpenRouterCatalog: #{e.class}: #{e.message}")
|
|
116
|
+
{ models_by_slug: {}, fetched_at: Time.now, source: "unavailable" }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def group_by_slug(raw_models)
|
|
120
|
+
grouped = raw_models.each_with_object({}) do |raw, acc|
|
|
121
|
+
slug, = raw["id"].to_s.split("/", 2)
|
|
122
|
+
next if slug.blank?
|
|
123
|
+
|
|
124
|
+
(acc[slug.downcase] ||= []) << normalize(raw, slug.downcase)
|
|
125
|
+
end
|
|
126
|
+
grouped.transform_values { |models| sort_models(models) }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Newest first: a live list has no curation.
|
|
130
|
+
def sort_models(models)
|
|
131
|
+
models.sort_by { |model| [-model[:released_at].to_i, model[:name].to_s.downcase] }
|
|
132
|
+
.each { |model| model.delete(:released_at) }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def normalize(raw, slug)
|
|
136
|
+
architecture = raw["architecture"] || {}
|
|
137
|
+
inputs = Array(architecture["input_modalities"])
|
|
138
|
+
outputs = Array(architecture["output_modalities"])
|
|
139
|
+
pricing = raw["pricing"] || {}
|
|
140
|
+
|
|
141
|
+
{
|
|
142
|
+
id: strip_slug(raw["id"]),
|
|
143
|
+
openrouter_id: raw["id"],
|
|
144
|
+
name: display_name(raw, slug),
|
|
145
|
+
model_type: outputs.include?("image") ? "vision" : "llm",
|
|
146
|
+
type: outputs.include?("image") ? "image" : "completion",
|
|
147
|
+
tasks: derive_tasks(raw, outputs),
|
|
148
|
+
capabilities: derive_capabilities(raw, inputs, pricing),
|
|
149
|
+
context_window: raw["context_length"]&.to_i,
|
|
150
|
+
max_output: raw.dig("top_provider", "max_completion_tokens")&.to_i,
|
|
151
|
+
pricing: {
|
|
152
|
+
input: per_million(pricing["prompt"]),
|
|
153
|
+
output: per_million(pricing["completion"]),
|
|
154
|
+
cached_read: per_million(pricing["input_cache_read"]),
|
|
155
|
+
cached_write: per_million(pricing["input_cache_write"]),
|
|
156
|
+
unit: "per_1m_tokens"
|
|
157
|
+
},
|
|
158
|
+
availability: "available",
|
|
159
|
+
unavailable_reason: nil,
|
|
160
|
+
released_at: raw["created"]
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Ids are "<provider-slug>/<model-slug>", and the slug is OpenRouter's own
|
|
165
|
+
# normalization — a provider may pin the real one.
|
|
166
|
+
def strip_slug(id)
|
|
167
|
+
id.to_s.split("/", 2).last.to_s
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Names are prefixed with the vendor ("OpenAI: GPT-4o") and the UI already
|
|
171
|
+
# shows it. Drop it only when the prefix really is the provider.
|
|
172
|
+
def display_name(raw, slug)
|
|
173
|
+
name = raw["name"].presence || strip_slug(raw["id"])
|
|
174
|
+
prefix, remainder = name.split(":", 2)
|
|
175
|
+
return name if remainder.blank?
|
|
176
|
+
return name unless prefix.to_s.downcase.gsub(/[^a-z0-9]/, "") == slug.gsub(/[^a-z0-9]/, "")
|
|
177
|
+
|
|
178
|
+
remainder.strip
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def derive_tasks(raw, outputs)
|
|
182
|
+
tasks = []
|
|
183
|
+
tasks << "Text Generation" if outputs.include?("text")
|
|
184
|
+
tasks << "Text-to-Image" if outputs.include?("image")
|
|
185
|
+
params = Array(raw["supported_parameters"])
|
|
186
|
+
tasks << "Reasoning" if params.include?("reasoning") || params.include?("include_reasoning")
|
|
187
|
+
tasks.uniq
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def derive_capabilities(raw, inputs, pricing)
|
|
191
|
+
params = Array(raw["supported_parameters"])
|
|
192
|
+
capabilities = []
|
|
193
|
+
capabilities << "Image analysis" if inputs.include?("image")
|
|
194
|
+
capabilities << "Documents" if inputs.include?("file")
|
|
195
|
+
capabilities << "Audio" if inputs.include?("audio")
|
|
196
|
+
capabilities << "Web search" if pricing["web_search"].present?
|
|
197
|
+
capabilities << "Tool use" if params.include?("tools")
|
|
198
|
+
capabilities << "Structured output" if params.include?("structured_outputs")
|
|
199
|
+
capabilities
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Quoted per token as strings; the contract is per million. A genuine zero
|
|
203
|
+
# stays 0.0, a missing field stays nil so the UI renders "—" not "$0".
|
|
204
|
+
def per_million(value)
|
|
205
|
+
return nil if value.nil? || value.to_s.strip.empty?
|
|
206
|
+
|
|
207
|
+
(value.to_f * PER_MILLION).round(4)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
@@ -155,6 +155,60 @@
|
|
|
155
155
|
},
|
|
156
156
|
"additionalProperties": true
|
|
157
157
|
},
|
|
158
|
+
"ModelCatalogType": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"enum": ["llm", "embedding", "vision"]
|
|
161
|
+
},
|
|
162
|
+
"ModelCatalogKind": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"enum": ["completion", "embedding", "image"]
|
|
165
|
+
},
|
|
166
|
+
"ModelCatalogAvailability": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"enum": ["available", "unavailable"]
|
|
169
|
+
},
|
|
170
|
+
"ModelCatalogPricing": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"properties": {
|
|
173
|
+
"input": { "type": ["number", "null"] },
|
|
174
|
+
"output": { "type": ["number", "null"] },
|
|
175
|
+
"cached_read": { "type": ["number", "null"] },
|
|
176
|
+
"cached_write": { "type": ["number", "null"] },
|
|
177
|
+
"unit": { "type": ["string", "null"], "default": "per_1m_tokens" }
|
|
178
|
+
},
|
|
179
|
+
"additionalProperties": true
|
|
180
|
+
},
|
|
181
|
+
"ModelCatalogEntry": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"properties": {
|
|
184
|
+
"id": { "type": "string" },
|
|
185
|
+
"name": { "type": "string" },
|
|
186
|
+
"model_type": { "$ref": "#/definitions/ModelCatalogType" },
|
|
187
|
+
"type": { "anyOf": [{ "$ref": "#/definitions/ModelCatalogKind" }, { "type": "null" }] },
|
|
188
|
+
"openrouter_id": { "type": ["string", "null"] },
|
|
189
|
+
"tasks": { "type": "array", "items": { "type": "string" } },
|
|
190
|
+
"capabilities": { "type": "array", "items": { "type": "string" } },
|
|
191
|
+
"context_window": { "type": ["integer", "null"] },
|
|
192
|
+
"max_output": { "type": ["integer", "null"] },
|
|
193
|
+
"pricing": { "anyOf": [{ "$ref": "#/definitions/ModelCatalogPricing" }, { "type": "null" }] },
|
|
194
|
+
"availability": { "$ref": "#/definitions/ModelCatalogAvailability" },
|
|
195
|
+
"unavailable_reason": { "type": ["string", "null"] }
|
|
196
|
+
},
|
|
197
|
+
"required": ["id", "name", "model_type"],
|
|
198
|
+
"additionalProperties": true
|
|
199
|
+
},
|
|
200
|
+
"ModelCatalog": {
|
|
201
|
+
"type": "object",
|
|
202
|
+
"properties": {
|
|
203
|
+
"models": {
|
|
204
|
+
"type": "array",
|
|
205
|
+
"items": {
|
|
206
|
+
"$ref": "#/definitions/ModelCatalogEntry"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"additionalProperties": true
|
|
211
|
+
},
|
|
158
212
|
"SyncConfig": {
|
|
159
213
|
"type": "object",
|
|
160
214
|
"properties": {
|
|
@@ -18,8 +18,11 @@ module Multiwoven
|
|
|
18
18
|
MultiwovenMessageType = Types::String.enum(
|
|
19
19
|
"record", "log", "connector_spec",
|
|
20
20
|
"connection_status", "catalog", "control",
|
|
21
|
-
"tracking"
|
|
21
|
+
"tracking", "model_catalog"
|
|
22
22
|
)
|
|
23
|
+
ModelCatalogType = Types::String.enum("llm", "embedding", "vision")
|
|
24
|
+
ModelCatalogKind = Types::String.enum("completion", "embedding", "image")
|
|
25
|
+
ModelCatalogAvailability = Types::String.default("available").enum("available", "unavailable")
|
|
23
26
|
ControlMessageType = Types::String.enum(
|
|
24
27
|
"rate_limit", "connection_config", "full_refresh"
|
|
25
28
|
)
|
|
@@ -166,6 +169,40 @@ module Multiwoven
|
|
|
166
169
|
end
|
|
167
170
|
end
|
|
168
171
|
|
|
172
|
+
class ModelCatalogPricing < ProtocolModel
|
|
173
|
+
attribute? :input, Types::Coercible::Float.optional
|
|
174
|
+
attribute? :output, Types::Coercible::Float.optional
|
|
175
|
+
attribute? :cached_read, Types::Coercible::Float.optional
|
|
176
|
+
attribute? :cached_write, Types::Coercible::Float.optional
|
|
177
|
+
attribute? :unit, Types::String.optional.default("per_1m_tokens")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
class ModelCatalogEntry < ProtocolModel
|
|
181
|
+
attribute :id, Types::String
|
|
182
|
+
attribute :name, Types::String
|
|
183
|
+
attribute :model_type, ModelCatalogType
|
|
184
|
+
attribute? :type, ModelCatalogKind.optional
|
|
185
|
+
attribute? :openrouter_id, Types::String.optional
|
|
186
|
+
attribute :tasks, Types::Array.of(Types::String).default([].freeze)
|
|
187
|
+
attribute :capabilities, Types::Array.of(Types::String).default([].freeze)
|
|
188
|
+
attribute? :context_window, Types::Integer.optional
|
|
189
|
+
attribute? :max_output, Types::Integer.optional
|
|
190
|
+
attribute? :pricing, ModelCatalogPricing.optional
|
|
191
|
+
attribute :availability, ModelCatalogAvailability
|
|
192
|
+
attribute? :unavailable_reason, Types::String.optional
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class ModelCatalog < ProtocolModel
|
|
196
|
+
attribute :models, Types::Array.of(ModelCatalogEntry).default([].freeze)
|
|
197
|
+
|
|
198
|
+
def to_multiwoven_message
|
|
199
|
+
MultiwovenMessage.new(
|
|
200
|
+
type: MultiwovenMessageType["model_catalog"],
|
|
201
|
+
model_catalog: self
|
|
202
|
+
)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
169
206
|
class IncrementStrategyConfig < ProtocolModel
|
|
170
207
|
attr_accessor :offset, :limit, :offset_variable, :limit_variable
|
|
171
208
|
|
|
@@ -230,6 +267,7 @@ module Multiwoven
|
|
|
230
267
|
attribute? :connection_status, ConnectionStatus.optional
|
|
231
268
|
attribute? :connector_spec, ConnectorSpecification.optional
|
|
232
269
|
attribute? :catalog, Catalog.optional
|
|
270
|
+
attribute? :model_catalog, ModelCatalog.optional
|
|
233
271
|
attribute? :record, RecordMessage.optional
|
|
234
272
|
attribute? :control, ControlMessage.optional
|
|
235
273
|
attribute? :tracking, TrackingMessage.optional
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Multiwoven
|
|
4
4
|
module Integrations
|
|
5
|
-
VERSION = "0.
|
|
5
|
+
VERSION = "0.41.0"
|
|
6
6
|
|
|
7
7
|
ENABLED_SOURCES = %w[
|
|
8
8
|
Snowflake
|
|
@@ -27,6 +27,8 @@ module Multiwoven
|
|
|
27
27
|
Anthropic
|
|
28
28
|
AwsBedrockModel
|
|
29
29
|
GenericOpenAI
|
|
30
|
+
GoogleGemini
|
|
31
|
+
JohnSnowLabs
|
|
30
32
|
IntuitQuickBooks
|
|
31
33
|
PineconeDB
|
|
32
34
|
Qdrant
|
|
@@ -7,15 +7,17 @@ module Multiwoven
|
|
|
7
7
|
yield(self.class.config) if block_given?
|
|
8
8
|
end
|
|
9
9
|
class << self
|
|
10
|
+
# deep_dup, not dup: the entries are the clients' own memoized metadata, so a
|
|
11
|
+
# shallow copy lets one caller's mutation reach every later caller.
|
|
10
12
|
def connectors
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
built = built_connectors
|
|
14
|
+
{ source: built[:source].deep_dup, destination: built[:destination].deep_dup }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# The build is memoized for the life of the process, so anything that
|
|
18
|
+
# changes what would be built has to clear it.
|
|
19
|
+
def reset_connectors!
|
|
20
|
+
@built_connectors = nil
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def connector_class(connector_type, connector_name)
|
|
@@ -38,6 +40,13 @@ module Multiwoven
|
|
|
38
40
|
|
|
39
41
|
private
|
|
40
42
|
|
|
43
|
+
def built_connectors
|
|
44
|
+
@built_connectors ||= {
|
|
45
|
+
source: build_connectors(ENABLED_SOURCES, "Source"),
|
|
46
|
+
destination: build_connectors(ENABLED_DESTINATIONS, "Destination")
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
def build_connectors(enabled_connectors, type)
|
|
42
51
|
enabled_connectors.map do |connector|
|
|
43
52
|
client = connector_class(type, connector).new
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"data": {
|
|
3
3
|
"name": "Aisquared",
|
|
4
|
-
"title": "
|
|
4
|
+
"title": "AISquared",
|
|
5
5
|
"connector_type": "source",
|
|
6
6
|
"category": "AI Model",
|
|
7
7
|
"sub_category": "LLM",
|
|
8
|
+
"provider_type": "built_in",
|
|
9
|
+
"display_order": 10,
|
|
10
|
+
"description": "AISquared's own model portfolio \u2014 Bolt Instruct, Vision and Embedding models. Pre-configured with no setup required.",
|
|
8
11
|
"documentation_url": "https://docs.squared.ai/activation/ai-ml-sources/aisquared-bolt",
|
|
9
12
|
"github_issue_label": "source-aisquared-bolt",
|
|
10
13
|
"icon": "icon.svg",
|
|
11
14
|
"license": "MIT",
|
|
12
15
|
"release_stage": "alpha",
|
|
13
16
|
"support_level": "community",
|
|
14
|
-
"tags": [
|
|
17
|
+
"tags": [
|
|
18
|
+
"language:ruby",
|
|
19
|
+
"multiwoven"
|
|
20
|
+
]
|
|
15
21
|
}
|
|
16
22
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"models": [
|
|
3
|
+
{
|
|
4
|
+
"id": "bolt-instruct-1b",
|
|
5
|
+
"name": "Bolt-Instruct-1B",
|
|
6
|
+
"model_type": "llm",
|
|
7
|
+
"type": "completion",
|
|
8
|
+
"tasks": [
|
|
9
|
+
"Text Classification",
|
|
10
|
+
"Guardrails"
|
|
11
|
+
],
|
|
12
|
+
"context_window": 32000,
|
|
13
|
+
"max_output": 4096,
|
|
14
|
+
"pricing": {
|
|
15
|
+
"input": 0.0,
|
|
16
|
+
"output": 0.0,
|
|
17
|
+
"cached_read": 0.0,
|
|
18
|
+
"cached_write": 0.0,
|
|
19
|
+
"unit": "per_1m_tokens"
|
|
20
|
+
},
|
|
21
|
+
"capabilities": [
|
|
22
|
+
"Documents"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "bolt-instruct-7b",
|
|
27
|
+
"name": "Bolt-Instruct-7B",
|
|
28
|
+
"model_type": "llm",
|
|
29
|
+
"type": "completion",
|
|
30
|
+
"tasks": [
|
|
31
|
+
"Text Generation",
|
|
32
|
+
"Routing",
|
|
33
|
+
"Orchestration"
|
|
34
|
+
],
|
|
35
|
+
"context_window": 32000,
|
|
36
|
+
"max_output": 8192,
|
|
37
|
+
"pricing": {
|
|
38
|
+
"input": 0.0,
|
|
39
|
+
"output": 0.0,
|
|
40
|
+
"cached_read": 0.0,
|
|
41
|
+
"cached_write": 0.0,
|
|
42
|
+
"unit": "per_1m_tokens"
|
|
43
|
+
},
|
|
44
|
+
"capabilities": [
|
|
45
|
+
"Documents",
|
|
46
|
+
"Data analyst"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "bolt-instruct-32b",
|
|
51
|
+
"name": "Bolt-Instruct-32B",
|
|
52
|
+
"model_type": "llm",
|
|
53
|
+
"type": "completion",
|
|
54
|
+
"tasks": [
|
|
55
|
+
"Text Generation",
|
|
56
|
+
"Reasoning"
|
|
57
|
+
],
|
|
58
|
+
"context_window": 128000,
|
|
59
|
+
"max_output": 16384,
|
|
60
|
+
"pricing": {
|
|
61
|
+
"input": 0.0,
|
|
62
|
+
"output": 0.0,
|
|
63
|
+
"cached_read": 0.0,
|
|
64
|
+
"cached_write": 0.0,
|
|
65
|
+
"unit": "per_1m_tokens"
|
|
66
|
+
},
|
|
67
|
+
"capabilities": [
|
|
68
|
+
"Documents",
|
|
69
|
+
"Data analyst"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "bolt-embedding-large",
|
|
74
|
+
"name": "Bolt-Embedding-Large",
|
|
75
|
+
"model_type": "embedding",
|
|
76
|
+
"type": "embedding",
|
|
77
|
+
"tasks": [
|
|
78
|
+
"Text Embeddings",
|
|
79
|
+
"Indexing",
|
|
80
|
+
"Retrieval"
|
|
81
|
+
],
|
|
82
|
+
"context_window": 8192,
|
|
83
|
+
"max_output": null,
|
|
84
|
+
"pricing": {
|
|
85
|
+
"input": 0.0,
|
|
86
|
+
"output": null,
|
|
87
|
+
"cached_read": null,
|
|
88
|
+
"cached_write": null,
|
|
89
|
+
"unit": "per_1m_tokens"
|
|
90
|
+
},
|
|
91
|
+
"capabilities": [
|
|
92
|
+
"Documents"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"id": "bolt-vision-9b",
|
|
97
|
+
"name": "Bolt-Vision-9B",
|
|
98
|
+
"model_type": "vision",
|
|
99
|
+
"type": "completion",
|
|
100
|
+
"tasks": [
|
|
101
|
+
"Image-to-Text",
|
|
102
|
+
"OCR",
|
|
103
|
+
"Document Parsing"
|
|
104
|
+
],
|
|
105
|
+
"context_window": 32000,
|
|
106
|
+
"max_output": 8192,
|
|
107
|
+
"pricing": {
|
|
108
|
+
"input": 0.0,
|
|
109
|
+
"output": 0.0,
|
|
110
|
+
"cached_read": 0.0,
|
|
111
|
+
"cached_write": 0.0,
|
|
112
|
+
"unit": "per_1m_tokens"
|
|
113
|
+
},
|
|
114
|
+
"capabilities": [
|
|
115
|
+
"Documents",
|
|
116
|
+
"Image analysis"
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
7
7
|
"title": "AI Squared Bolt",
|
|
8
8
|
"type": "object",
|
|
9
|
-
"
|
|
9
|
+
"x-dynamic-input-path": "messages.0.content",
|
|
10
|
+
"x-output-path": "choices.0.message.content",
|
|
11
|
+
"required": [
|
|
12
|
+
"request_format",
|
|
13
|
+
"response_format"
|
|
14
|
+
],
|
|
10
15
|
"properties": {
|
|
11
16
|
"request_format": {
|
|
12
17
|
"title": "Request Format",
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"data": {
|
|
3
3
|
"name": "Anthropic",
|
|
4
|
-
"title": "Anthropic
|
|
4
|
+
"title": "Anthropic",
|
|
5
5
|
"connector_type": "source",
|
|
6
6
|
"category": "AI Model",
|
|
7
7
|
"sub_category": "LLM",
|
|
8
|
+
"provider_type": "third_party",
|
|
9
|
+
"display_order": 30,
|
|
10
|
+
"openrouter_slug": "anthropic",
|
|
11
|
+
"description": "Claude model family for nuanced reasoning, analysis, and generation. Strong safety alignment and instruction-following across all model sizes.",
|
|
8
12
|
"documentation_url": "https://docs.squared.ai/activation/ai-ml-sources/anthropic-model",
|
|
9
13
|
"github_issue_label": "source-anthropic-model",
|
|
10
14
|
"icon": "icon.svg",
|
|
11
15
|
"license": "MIT",
|
|
12
16
|
"release_stage": "alpha",
|
|
13
17
|
"support_level": "community",
|
|
14
|
-
"tags": [
|
|
18
|
+
"tags": [
|
|
19
|
+
"language:ruby",
|
|
20
|
+
"multiwoven"
|
|
21
|
+
]
|
|
15
22
|
}
|
|
16
23
|
}
|