lex-claude 0.1.2 → 0.3.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.
@@ -9,19 +9,64 @@ module Legion
9
9
  module Helpers
10
10
  module Client
11
11
  DEFAULT_HOST = 'https://api.anthropic.com'
12
- API_VERSION = '2023-06-01'
12
+ API_VERSION = '2023-06-01'
13
+
14
+ BETA_HEADERS = {
15
+ interleaved_thinking: 'interleaved-thinking-2025-05-14',
16
+ context_1m: 'context-1m-2025-08-07',
17
+ context_management: 'context-management-2025-06-27',
18
+ structured_outputs: 'structured-outputs-2025-12-15',
19
+ web_search: 'web-search-2025-03-05',
20
+ advanced_tool_use: 'advanced-tool-use-2025-11-20',
21
+ effort: 'effort-2025-11-24',
22
+ task_budgets: 'task-budgets-2026-03-13',
23
+ prompt_caching_scope: 'prompt-caching-scope-2026-01-05',
24
+ fast_mode: 'fast-mode-2026-02-01',
25
+ redact_thinking: 'redact-thinking-2026-02-12',
26
+ token_efficient_tools: 'token-efficient-tools-2026-03-28',
27
+ summarize_connector: 'summarize-connector-text-2026-03-13',
28
+ afk_mode: 'afk-mode-2026-01-31',
29
+ advisor: 'advisor-tool-2026-03-01',
30
+ files_api: 'files-api-2025-04-14',
31
+ claude_code: 'claude-code-20250219',
32
+ tool_search: 'tool-search-tool-2025-10-19'
33
+ }.freeze
13
34
 
14
35
  module_function
15
36
 
16
- def client(api_key:, host: DEFAULT_HOST, **_opts)
37
+ def client(api_key:, host: DEFAULT_HOST, betas: nil, **_opts)
38
+ beta_list = resolve_betas(betas)
39
+
17
40
  Faraday.new(url: host) do |conn|
18
41
  conn.request :json
19
42
  conn.response :json, content_type: /\bjson$/
20
43
  conn.headers['x-api-key'] = api_key
21
- conn.headers['anthropic-version'] = API_VERSION
22
- conn.headers['Content-Type'] = 'application/json'
44
+ conn.headers['anthropic-version'] = API_VERSION
45
+ conn.headers['Content-Type'] = 'application/json'
46
+ conn.headers['anthropic-beta'] = beta_list.join(',') if beta_list.any?
23
47
  end
24
48
  end
49
+
50
+ def streaming_client(api_key:, host: DEFAULT_HOST, betas: nil, **_opts)
51
+ beta_list = resolve_betas(betas)
52
+
53
+ Faraday.new(url: host) do |conn|
54
+ conn.headers['x-api-key'] = api_key
55
+ conn.headers['anthropic-version'] = API_VERSION
56
+ conn.headers['Content-Type'] = 'application/json'
57
+ conn.headers['Accept'] = 'text/event-stream'
58
+ conn.headers['anthropic-beta'] = beta_list.join(',') if beta_list.any?
59
+ conn.adapter Faraday.default_adapter
60
+ end
61
+ end
62
+
63
+ def resolve_betas(betas)
64
+ return [] if betas.nil? || betas.empty?
65
+
66
+ betas.filter_map do |b|
67
+ b.is_a?(Symbol) ? BETA_HEADERS[b] : b.to_s
68
+ end.uniq
69
+ end
25
70
  end
26
71
  end
27
72
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Claude
6
+ module Helpers
7
+ module Errors
8
+ class ApiError < StandardError
9
+ attr_reader :status, :error_type, :body
10
+
11
+ def initialize(message = nil, status: nil, error_type: nil, body: nil)
12
+ super(message)
13
+ @status = status
14
+ @error_type = error_type
15
+ @body = body
16
+ end
17
+ end
18
+
19
+ class AuthenticationError < ApiError; end
20
+ class PermissionError < ApiError; end
21
+ class NotFoundError < ApiError; end
22
+ class RateLimitError < ApiError; end
23
+ class OverloadedError < ApiError; end
24
+ class InvalidRequestError < ApiError; end
25
+ class ServerError < ApiError; end
26
+ class StreamingError < ApiError; end
27
+
28
+ STATUS_MAP = {
29
+ 401 => AuthenticationError,
30
+ 403 => PermissionError,
31
+ 404 => NotFoundError,
32
+ 429 => RateLimitError,
33
+ 529 => OverloadedError
34
+ }.freeze
35
+
36
+ TYPE_MAP = {
37
+ 'authentication_error' => AuthenticationError,
38
+ 'permission_error' => PermissionError,
39
+ 'not_found_error' => NotFoundError,
40
+ 'rate_limit_error' => RateLimitError,
41
+ 'overloaded_error' => OverloadedError,
42
+ 'invalid_request_error' => InvalidRequestError,
43
+ 'server_error' => ServerError,
44
+ 'streaming_error' => StreamingError
45
+ }.freeze
46
+
47
+ RETRYABLE = [RateLimitError, OverloadedError].freeze
48
+
49
+ module_function
50
+
51
+ def from_response(status:, body:)
52
+ error_hash = body.is_a?(Hash) ? (body[:error] || body['error']) : nil # rubocop:disable Legion/Framework/ApiStringKeys
53
+ error_type = error_hash.is_a?(Hash) ? (error_hash[:type] || error_hash['type']) : nil
54
+ message = error_hash.is_a?(Hash) ? (error_hash[:message] || error_hash['message']) : nil
55
+ message ||= body.to_s
56
+
57
+ klass = TYPE_MAP[error_type] ||
58
+ STATUS_MAP[status] ||
59
+ (status >= 500 ? ServerError : InvalidRequestError)
60
+
61
+ klass.new(message, status: status, error_type: error_type, body: body)
62
+ end
63
+
64
+ def retryable?(error)
65
+ RETRYABLE.any? { |klass| error.is_a?(klass) }
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Claude
6
+ module Helpers
7
+ module Models
8
+ # rubocop:disable Naming/VariableNumber
9
+ MODELS = {
10
+ haiku_3_5: 'claude-3-5-haiku-20241022',
11
+ haiku_4_5: 'claude-haiku-4-5-20251001',
12
+ sonnet_3_5: 'claude-3-5-sonnet-20241022',
13
+ sonnet_3_7: 'claude-3-7-sonnet-20250219',
14
+ sonnet_4: 'claude-sonnet-4-20250514',
15
+ sonnet_4_5: 'claude-sonnet-4-5-20250929',
16
+ sonnet_4_6: 'claude-sonnet-4-6',
17
+ opus_4: 'claude-opus-4-20250514',
18
+ opus_4_1: 'claude-opus-4-1-20250805',
19
+ opus_4_5: 'claude-opus-4-5-20251101',
20
+ opus_4_6: 'claude-opus-4-6'
21
+ }.freeze
22
+ # rubocop:enable Naming/VariableNumber
23
+
24
+ ADAPTIVE_THINKING_MODELS = %w[
25
+ claude-sonnet-4-20250514
26
+ claude-sonnet-4-5-20250929
27
+ claude-sonnet-4-6
28
+ claude-opus-4-20250514
29
+ claude-opus-4-1-20250805
30
+ claude-opus-4-5-20251101
31
+ claude-opus-4-6
32
+ ].freeze
33
+
34
+ module_function
35
+
36
+ def resolve(model)
37
+ key = model.is_a?(Symbol) ? model : model.to_s.to_sym
38
+ MODELS.fetch(key, model.to_s)
39
+ end
40
+
41
+ def adaptive_thinking?(model_id)
42
+ ADAPTIVE_THINKING_MODELS.include?(model_id.to_s)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/claude/helpers/errors'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Claude
8
+ module Helpers
9
+ module Response
10
+ RATE_LIMIT_HEADERS = {
11
+ 'anthropic-ratelimit-unified-status' => :status,
12
+ 'anthropic-ratelimit-unified-reset' => :reset,
13
+ 'anthropic-ratelimit-unified-fallback' => :fallback,
14
+ 'anthropic-ratelimit-unified-5h-utilization' => :utilization_5h,
15
+ 'anthropic-ratelimit-unified-5h-reset' => :reset_5h,
16
+ 'anthropic-ratelimit-unified-7d-utilization' => :utilization_7d,
17
+ 'anthropic-ratelimit-unified-7d-reset' => :reset_7d,
18
+ 'anthropic-ratelimit-unified-overage-status' => :overage_status,
19
+ 'anthropic-ratelimit-unified-overage-reset' => :overage_reset
20
+ }.freeze
21
+
22
+ FLOAT_KEYS = %i[utilization_5h utilization_7d].freeze
23
+
24
+ module_function
25
+
26
+ def handle_response(response)
27
+ raise Errors.from_response(status: response.status, body: response.body) unless response.status >= 200 && response.status < 300
28
+
29
+ result = { result: response.body, status: response.status }
30
+ rate_info = parse_rate_limit_headers(response.headers)
31
+ result[:rate_limit] = rate_info unless rate_info.empty?
32
+ result
33
+ end
34
+
35
+ def parse_rate_limit_headers(headers)
36
+ return {} if headers.nil? || headers.empty?
37
+
38
+ parsed = {}
39
+ RATE_LIMIT_HEADERS.each do |header_name, key|
40
+ value = headers[header_name]
41
+ next if value.nil?
42
+
43
+ parsed[key] = FLOAT_KEYS.include?(key) ? value.to_f : value
44
+ end
45
+ parsed
46
+ end
47
+
48
+ def parse_usage(body)
49
+ usage = body.is_a?(Hash) ? (body[:usage] || body['usage'] || {}) : {} # rubocop:disable Legion/Framework/ApiStringKeys
50
+ {
51
+ input_tokens: (usage[:input_tokens] || usage['input_tokens'] || 0).to_i,
52
+ output_tokens: (usage[:output_tokens] || usage['output_tokens'] || 0).to_i,
53
+ cache_read_tokens: (usage[:cache_read_input_tokens] || usage['cache_read_input_tokens'] || 0).to_i,
54
+ cache_write_tokens: (usage[:cache_creation_input_tokens] || usage['cache_creation_input_tokens'] || 0).to_i
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/claude/helpers/errors'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Claude
8
+ module Helpers
9
+ module Retry
10
+ DEFAULT_MAX_ATTEMPTS = 3
11
+ DEFAULT_BASE_DELAY = 1.0
12
+ DEFAULT_MAX_DELAY = 60.0
13
+
14
+ module_function
15
+
16
+ def with_retry(max_attempts: DEFAULT_MAX_ATTEMPTS, base_delay: DEFAULT_BASE_DELAY,
17
+ max_delay: DEFAULT_MAX_DELAY)
18
+ attempt = 0
19
+ begin
20
+ yield
21
+ rescue Errors::ApiError => e
22
+ raise unless Errors.retryable?(e)
23
+
24
+ attempt += 1
25
+ raise if attempt >= max_attempts
26
+
27
+ delay = backoff_seconds(attempt: attempt - 1, base_delay: base_delay, max_delay: max_delay)
28
+ sleep(delay) if delay.positive?
29
+ retry
30
+ end
31
+ end
32
+
33
+ def backoff_seconds(attempt:, base_delay: DEFAULT_BASE_DELAY, max_delay: DEFAULT_MAX_DELAY)
34
+ raw = base_delay * (2**attempt)
35
+ [raw, max_delay].min.to_f
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Claude
8
+ module Helpers
9
+ module Sse
10
+ module_function
11
+
12
+ def parse_stream(raw, include_pings: false)
13
+ events = []
14
+ current_event = nil
15
+
16
+ raw.each_line do |line|
17
+ line = line.chomp
18
+ if line.start_with?('event:')
19
+ current_event = line.sub(/^event:\s*/, '').strip
20
+ elsif line.start_with?('data:')
21
+ next if current_event == 'ping' && !include_pings
22
+
23
+ json_str = line.sub(/^data:\s*/, '').strip
24
+ next if json_str.empty?
25
+
26
+ begin
27
+ data = MultiJson.load(json_str)
28
+ events << { event: current_event, data: data }
29
+ rescue MultiJson::ParseError => e
30
+ log.warn("SSE parse error: #{e.message}")
31
+ next
32
+ end
33
+ current_event = nil
34
+ end
35
+ end
36
+
37
+ events
38
+ end
39
+
40
+ def collect_text(events)
41
+ events
42
+ .select { |e| e[:event] == 'content_block_delta' && e[:data].dig('delta', 'type') == 'text_delta' }
43
+ .map { |e| e[:data].dig('delta', 'text').to_s }
44
+ .join
45
+ end
46
+
47
+ def collect_usage(events)
48
+ input_tokens = 0
49
+ output_tokens = 0
50
+
51
+ events.each do |e|
52
+ case e[:event]
53
+ when 'message_start'
54
+ usage = e[:data].dig('message', 'usage') || {}
55
+ input_tokens += usage.fetch('input_tokens', 0).to_i
56
+ output_tokens += usage.fetch('output_tokens', 0).to_i
57
+ when 'message_delta'
58
+ usage = e[:data].fetch('usage', {})
59
+ output_tokens += usage.fetch('output_tokens', 0).to_i
60
+ end
61
+ end
62
+
63
+ { input_tokens: input_tokens, output_tokens: output_tokens }
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Claude
6
+ module Helpers
7
+ module Tools
8
+ module_function
9
+
10
+ def web_search(max_uses: 5, allowed_domains: nil, blocked_domains: nil)
11
+ tool = { type: 'web_search_20250305', max_uses: max_uses }
12
+ tool[:allowed_domains] = allowed_domains if allowed_domains
13
+ tool[:blocked_domains] = blocked_domains if blocked_domains
14
+ tool
15
+ end
16
+
17
+ def cache_control
18
+ { type: 'ephemeral' }
19
+ end
20
+
21
+ def required_betas_for(tools)
22
+ return [] if tools.nil? || tools.empty?
23
+
24
+ betas = []
25
+ betas << :web_search if tools.any? { |t| t.is_a?(Hash) && t[:type].to_s.start_with?('web_search') }
26
+ betas
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/claude/helpers/client'
4
+ require 'legion/extensions/claude/helpers/response'
4
5
 
5
6
  module Legion
6
7
  module Extensions
@@ -12,7 +13,7 @@ module Legion
12
13
  def create_batch(api_key:, requests:, **)
13
14
  body = { requests: requests }
14
15
  response = client(api_key: api_key, **).post('/v1/messages/batches', body)
15
- { result: response.body, status: response.status }
16
+ Helpers::Response.handle_response(response)
16
17
  end
17
18
 
18
19
  def list_batches(api_key:, limit: 20, before_id: nil, after_id: nil, **)
@@ -21,26 +22,26 @@ module Legion
21
22
  params[:after_id] = after_id if after_id
22
23
 
23
24
  response = client(api_key: api_key, **).get('/v1/messages/batches', params)
24
- { result: response.body, status: response.status }
25
+ Helpers::Response.handle_response(response)
25
26
  end
26
27
 
27
28
  def retrieve_batch(api_key:, batch_id:, **)
28
29
  response = client(api_key: api_key, **).get("/v1/messages/batches/#{batch_id}")
29
- { result: response.body, status: response.status }
30
+ Helpers::Response.handle_response(response)
30
31
  end
31
32
 
32
33
  def cancel_batch(api_key:, batch_id:, **)
33
34
  response = client(api_key: api_key, **).post("/v1/messages/batches/#{batch_id}/cancel")
34
- { result: response.body, status: response.status }
35
+ Helpers::Response.handle_response(response)
35
36
  end
36
37
 
37
38
  def batch_results(api_key:, batch_id:, **)
38
39
  response = client(api_key: api_key, **).get("/v1/messages/batches/#{batch_id}/results")
39
- { result: response.body, status: response.status }
40
+ Helpers::Response.handle_response(response)
40
41
  end
41
42
 
42
- include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
43
- Legion::Extensions::Helpers.const_defined?(:Lex)
43
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
44
+ Legion::Extensions::Helpers.const_defined?(:Lex, false)
44
45
  end
45
46
  end
46
47
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/claude/helpers/client'
4
+ require 'legion/extensions/claude/helpers/response'
5
+ require 'legion/extensions/claude/helpers/sse'
4
6
 
5
7
  module Legion
6
8
  module Extensions
@@ -9,39 +11,109 @@ module Legion
9
11
  module Messages
10
12
  extend Legion::Extensions::Claude::Helpers::Client
11
13
 
12
- def create(api_key:, model:, messages:, max_tokens: 1024, system: nil, temperature: nil, # rubocop:disable Metrics/ParameterLists
13
- top_p: nil, top_k: nil, stop_sequences: nil, metadata: nil, tools: nil,
14
- tool_choice: nil, stream: false, **)
15
- body = {
16
- model: model,
17
- messages: messages,
18
- max_tokens: max_tokens,
19
- stream: stream
14
+ def create(api_key:, model:, messages:, max_tokens: 1024, stream: false, betas: nil, **opts)
15
+ body = build_message_body(model: model, messages: messages, max_tokens: max_tokens, stream: stream, **opts)
16
+ resolved_betas = resolve_feature_betas(betas, opts)
17
+
18
+ response = client(api_key: api_key, betas: resolved_betas, **opts).post('/v1/messages', body)
19
+ result = Helpers::Response.handle_response(response)
20
+ result[:usage] = Helpers::Response.parse_usage(response.body) if response.body.is_a?(Hash)
21
+ result
22
+ end
23
+
24
+ def create_stream(api_key:, model:, messages:, max_tokens: 1024, betas: nil, **opts, &block)
25
+ body = build_message_body(model: model, messages: messages, max_tokens: max_tokens, stream: true, **opts)
26
+ resolved_betas = resolve_feature_betas(betas, opts)
27
+
28
+ raw_body = +''
29
+ conn = Helpers::Client.streaming_client(api_key: api_key, betas: resolved_betas)
30
+ response = conn.post('/v1/messages', MultiJson.dump(body)) do |req|
31
+ req.options.on_data = proc { |chunk, _bytes| raw_body << chunk }
32
+ end
33
+
34
+ raise Helpers::Errors.from_response(status: response.status, body: {}) unless response.status == 200
35
+
36
+ raw_body = response.body if raw_body.empty? && response.body.is_a?(String)
37
+
38
+ events = Helpers::Sse.parse_stream(raw_body)
39
+ events.each(&block) if block
40
+
41
+ {
42
+ result: Helpers::Sse.collect_text(events),
43
+ events: events,
44
+ usage: Helpers::Sse.collect_usage(events),
45
+ status: 200
20
46
  }
21
- body[:system] = system if system
22
- body[:temperature] = temperature if temperature
23
- body[:top_p] = top_p if top_p
24
- body[:top_k] = top_k if top_k
25
- body[:stop_sequences] = stop_sequences if stop_sequences
26
- body[:metadata] = metadata if metadata
27
- body[:tools] = tools if tools
28
- body[:tool_choice] = tool_choice if tool_choice
29
-
30
- response = client(api_key: api_key, **).post('/v1/messages', body)
31
- { result: response.body, status: response.status }
32
47
  end
33
48
 
34
- def count_tokens(api_key:, model:, messages:, system: nil, tools: nil, **) # rubocop:disable Metrics/ParameterLists
49
+ def count_tokens(api_key:, model:, messages:, betas: nil, **opts)
50
+ system = opts[:system]
51
+ tools = opts[:tools]
52
+ thinking = opts[:thinking]
53
+ cache_system = opts.fetch(:cache_system, false)
54
+
35
55
  body = { model: model, messages: messages }
36
- body[:system] = system if system
37
- body[:tools] = tools if tools
56
+ body[:system] = build_system(system, cache_system) if system
57
+ body[:tools] = tools if tools
58
+ body[:thinking] = thinking if thinking
59
+
60
+ resolved_betas = Array(betas).dup
61
+ resolved_betas << :interleaved_thinking if thinking && !resolved_betas.include?(:interleaved_thinking)
62
+
63
+ response = client(api_key: api_key, betas: resolved_betas).post('/v1/messages/count_tokens', body)
64
+ Helpers::Response.handle_response(response)
65
+ end
66
+
67
+ private
68
+
69
+ def build_message_body(model:, messages:, max_tokens:, stream:, system: nil, temperature: nil, # rubocop:disable Metrics/ParameterLists
70
+ top_p: nil, top_k: nil, stop_sequences: nil, metadata: nil, tools: nil,
71
+ tool_choice: nil, cache_system: false, thinking: nil, output_config: nil,
72
+ fast_mode: false, context_management: nil, **)
73
+ body = { model: model, messages: messages, max_tokens: max_tokens, stream: stream }
74
+
75
+ body[:system] = build_system(system, cache_system) if system
76
+ body[:top_p] = top_p if top_p
77
+ body[:top_k] = top_k if top_k
78
+ body[:stop_sequences] = stop_sequences if stop_sequences
79
+ body[:metadata] = metadata if metadata
80
+ body[:tools] = tools if tools
81
+ body[:tool_choice] = tool_choice if tool_choice
82
+ body[:output_config] = output_config if output_config
83
+ body[:speed] = 'fast' if fast_mode
84
+ body[:context_management] = context_management if context_management
85
+
86
+ if thinking
87
+ body[:thinking] = thinking
88
+ elsif temperature
89
+ body[:temperature] = temperature
90
+ end
91
+
92
+ body
93
+ end
94
+
95
+ def resolve_feature_betas(betas, opts)
96
+ resolved = Array(betas).dup
97
+ resolved << :prompt_caching_scope if opts[:cache_scope] == :global
98
+ resolved << :interleaved_thinking if opts[:thinking] && !resolved.include?(:interleaved_thinking)
99
+ resolved << :structured_outputs if opts[:output_config]&.key?(:format)
100
+ resolved << :effort if opts[:output_config]&.key?(:effort)
101
+ resolved << :task_budgets if opts[:output_config]&.key?(:task_budget)
102
+ resolved << :fast_mode if opts[:fast_mode]
103
+ resolved << :context_management if opts[:context_management]
104
+ resolved
105
+ end
38
106
 
39
- response = client(api_key: api_key, **).post('/v1/messages/count_tokens', body)
40
- { result: response.body, status: response.status }
107
+ def build_system(system, cache_system)
108
+ if cache_system
109
+ [{ type: 'text', text: system, cache_control: { type: 'ephemeral' } }]
110
+ else
111
+ system
112
+ end
41
113
  end
42
114
 
43
- include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
44
- Legion::Extensions::Helpers.const_defined?(:Lex)
115
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
116
+ Legion::Extensions::Helpers.const_defined?(:Lex, false)
45
117
  end
46
118
  end
47
119
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/claude/helpers/client'
4
+ require 'legion/extensions/claude/helpers/response'
4
5
 
5
6
  module Legion
6
7
  module Extensions
@@ -15,16 +16,16 @@ module Legion
15
16
  params[:after_id] = after_id if after_id
16
17
 
17
18
  response = client(api_key: api_key, **).get('/v1/models', params)
18
- { result: response.body, status: response.status }
19
+ Helpers::Response.handle_response(response)
19
20
  end
20
21
 
21
22
  def retrieve(api_key:, model_id:, **)
22
23
  response = client(api_key: api_key, **).get("/v1/models/#{model_id}")
23
- { result: response.body, status: response.status }
24
+ Helpers::Response.handle_response(response)
24
25
  end
25
26
 
26
- include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
27
- Legion::Extensions::Helpers.const_defined?(:Lex)
27
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
28
+ Legion::Extensions::Helpers.const_defined?(:Lex, false)
28
29
  end
29
30
  end
30
31
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Claude
6
- VERSION = '0.1.2'
6
+ VERSION = '0.3.0'
7
7
  end
8
8
  end
9
9
  end
@@ -2,6 +2,12 @@
2
2
 
3
3
  require 'legion/extensions/claude/version'
4
4
  require 'legion/extensions/claude/helpers/client'
5
+ require 'legion/extensions/claude/helpers/errors'
6
+ require 'legion/extensions/claude/helpers/retry'
7
+ require 'legion/extensions/claude/helpers/sse'
8
+ require 'legion/extensions/claude/helpers/response'
9
+ require 'legion/extensions/claude/helpers/tools'
10
+ require 'legion/extensions/claude/helpers/models'
5
11
  require 'legion/extensions/claude/runners/messages'
6
12
  require 'legion/extensions/claude/runners/models'
7
13
  require 'legion/extensions/claude/runners/batches'
@@ -9,7 +15,7 @@ require 'legion/extensions/claude/runners/batches'
9
15
  module Legion
10
16
  module Extensions
11
17
  module Claude
12
- extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
18
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core, false
13
19
  end
14
20
  end
15
21
  end