summaryrefslogtreecommitdiffhomepage
path: root/lib/dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dispatch')
-rw-r--r--lib/dispatch/adapter/base.rb2
-rw-r--r--lib/dispatch/adapter/copilot.rb53
-rw-r--r--lib/dispatch/adapter/model_info.rb8
-rw-r--r--lib/dispatch/adapter/rate_limiter.rb9
4 files changed, 56 insertions, 16 deletions
diff --git a/lib/dispatch/adapter/base.rb b/lib/dispatch/adapter/base.rb
index dbd136a..4b7a6ed 100644
--- a/lib/dispatch/adapter/base.rb
+++ b/lib/dispatch/adapter/base.rb
@@ -11,7 +11,7 @@ module Dispatch
raise NotImplementedError, "#{self.class}#model_name must be implemented"
end
- def count_tokens(_messages, system: nil, tools: [])
+ def count_tokens(_messages, system: nil, tools: []) # rubocop:disable Lint/UnusedMethodArgument
-1
end
diff --git a/lib/dispatch/adapter/copilot.rb b/lib/dispatch/adapter/copilot.rb
index 728ce99..d8a54a0 100644
--- a/lib/dispatch/adapter/copilot.rb
+++ b/lib/dispatch/adapter/copilot.rb
@@ -116,23 +116,20 @@ module Dispatch
def list_models
ensure_authenticated!
@rate_limiter.wait!
- uri = URI("#{API_BASE}/v1/models")
+ uri = URI("#{API_BASE}/models")
request = Net::HTTP::Get.new(uri)
apply_headers!(request)
+ request["X-Github-Api-Version"] = "2025-10-01"
response = execute_request(uri, request)
data = parse_response!(response)
models = data["data"] || []
- models.map do |m|
- ModelInfo.new(
- id: m["id"],
- name: m["id"],
- max_context_tokens: MODEL_CONTEXT_WINDOWS.fetch(m["id"], 0),
- supports_vision: false,
- supports_tool_use: true,
- supports_streaming: true
- )
+ models.filter_map do |m|
+ next unless m["model_picker_enabled"]
+ next unless chat_model?(m)
+
+ build_model_info(m)
end
end
@@ -147,6 +144,40 @@ module Dispatch
"Invalid thinking level: #{level.inspect}. Must be one of: #{VALID_THINKING_LEVELS.join(", ")}, or nil"
end
+ def chat_model?(model_data)
+ capabilities = model_data["capabilities"]
+ return true unless capabilities
+
+ model_type = capabilities["type"]
+ return true if model_type.nil?
+
+ if model_type.is_a?(Array)
+ model_type.include?("chat")
+ else
+ model_type == "chat"
+ end
+ end
+
+ def build_model_info(model_data)
+ capabilities = model_data["capabilities"] || {}
+ supports = capabilities["supports"] || {}
+ limits = capabilities["limits"] || {}
+ billing = model_data["billing"] || {}
+
+ context_tokens = limits["max_context_window_tokens"] ||
+ MODEL_CONTEXT_WINDOWS.fetch(model_data["id"], 0)
+
+ ModelInfo.new(
+ id: model_data["id"],
+ name: model_data["name"] || model_data["id"],
+ max_context_tokens: context_tokens.to_i,
+ supports_vision: !!supports["vision"],
+ supports_tool_use: !!supports["tool_calls"],
+ supports_streaming: !!supports["streaming"],
+ premium_request_multiplier: billing["multiplier"]&.to_f
+ )
+ end
+
def default_token_path
File.join(Dir.home, ".config", "dispatch", "copilot_github_token")
end
@@ -571,7 +602,7 @@ module Dispatch
next if line.empty?
next unless line.start_with?("data: ")
- data_str = line.sub(/\Adata: /, "")
+ data_str = line.delete_prefix("data: ")
next if data_str == "[DONE]"
data = JSON.parse(data_str)
diff --git a/lib/dispatch/adapter/model_info.rb b/lib/dispatch/adapter/model_info.rb
index 73d8a35..8ba2977 100644
--- a/lib/dispatch/adapter/model_info.rb
+++ b/lib/dispatch/adapter/model_info.rb
@@ -5,7 +5,13 @@ module Dispatch
ModelInfo = Struct.new(
:id, :name, :max_context_tokens,
:supports_vision, :supports_tool_use, :supports_streaming,
+ :premium_request_multiplier,
keyword_init: true
- )
+ ) do
+ def initialize(id:, name:, max_context_tokens:, supports_vision:, supports_tool_use:, supports_streaming:,
+ premium_request_multiplier: nil)
+ super
+ end
+ end
end
end
diff --git a/lib/dispatch/adapter/rate_limiter.rb b/lib/dispatch/adapter/rate_limiter.rb
index 6f10905..8d0789e 100644
--- a/lib/dispatch/adapter/rate_limiter.rb
+++ b/lib/dispatch/adapter/rate_limiter.rb
@@ -20,6 +20,7 @@ module Dispatch
loop do
wait_time = 0.0
+ done = false
File.open(rate_limit_file, File::RDWR | File::CREAT) do |file|
file.flock(File::LOCK_EX)
@@ -30,10 +31,12 @@ module Dispatch
if wait_time <= 0
record_request(state, now)
write_state(file, state)
- return
+ done = true
end
end
+ return if done
+
sleep(wait_time)
end
end
@@ -99,7 +102,7 @@ module Dispatch
elapsed = now - last
remaining = interval - elapsed
- remaining > 0 ? remaining : 0.0
+ remaining.positive? ? remaining : 0.0
end
def compute_window_wait(state, now)
@@ -115,7 +118,7 @@ module Dispatch
oldest_in_window = log.min
wait = oldest_in_window + period - now
- wait > 0 ? wait : 0.0
+ wait.positive? ? wait : 0.0
end
def record_request(state, now)