Class: Bristow::Providers::Anthropic

Inherits:
Base
  • Object
show all
Defined in:
lib/bristow/providers/anthropic.rb

Instance Attribute Summary

Attributes inherited from Base

#api_key

Instance Method Summary collapse

Constructor Details

#initialize(api_key:) ⇒ Anthropic



4
5
6
7
# File 'lib/bristow/providers/anthropic.rb', line 4

def initialize(api_key:)
  super
  @client = ::Anthropic::Client.new(api_key: api_key)
end

Instance Method Details

#chat(params) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/bristow/providers/anthropic.rb', line 9

def chat(params)
  # Convert messages format for Anthropic
  anthropic_params = convert_params(params)

  response = @client.messages.create(anthropic_params)

  # Convert response back to standard format
  if response.content.any? { |content| content.type == "tool_use" }
    # Handle tool use response
    tool_use = response.content.find { |content| content.type == "tool_use" }
    {
      "role" => "assistant",
      "function_call" => {
        "name" => tool_use.name,
        "arguments" => tool_use.input.to_json
      }
    }
  else
    # Handle text response
    text_content = response.content.find { |content| content.type == "text" }&.text || ""
    {
      "role" => "assistant",
      "content" => text_content
    }
  end
end

#default_modelObject



106
107
108
# File 'lib/bristow/providers/anthropic.rb', line 106

def default_model
  "claude-3-5-sonnet-20241022"
end

#format_function_response(response, result) ⇒ Object



95
96
97
98
99
100
101
102
103
104
# File 'lib/bristow/providers/anthropic.rb', line 95

def format_function_response(response, result)
  {
    role: :user,
    content: [{
      type: "tool_result",
      tool_use_id: response[:content][0][:id],
      content: result.to_json
    }]
  }
end

#format_functions(functions) ⇒ Object



75
76
77
78
79
# File 'lib/bristow/providers/anthropic.rb', line 75

def format_functions(functions)
  {
    tools: functions.map { |func| convert_function_to_tool(func.to_schema) }
  }
end

#function_arguments(response) ⇒ Object



90
91
92
93
# File 'lib/bristow/providers/anthropic.rb', line 90

def function_arguments(response)
  # TODO: support parallel function calls
  response[:content][0][:input]
end

#function_name(response) ⇒ Object



85
86
87
88
# File 'lib/bristow/providers/anthropic.rb', line 85

def function_name(response)
  # TODO: support parallel function calls
  response[:content][0][:name]
end

#is_function_call?(response) ⇒ Boolean



81
82
83
# File 'lib/bristow/providers/anthropic.rb', line 81

def is_function_call?(response)
  response.dig(:content, 0, :type) == :tool_use
end

#stream_chat(params, &block) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/bristow/providers/anthropic.rb', line 36

def stream_chat(params, &block)
  anthropic_params = convert_params(params)

  full_content = ""
  tool_use = {}
  tool_use_content_json = ""

  stream = @client.messages.stream_raw(anthropic_params)
  stream.each do |event|
    case event.type
    when :content_block_delta
      if event.delta.type == :text_delta
        text = event.delta.text
        full_content += text
        yield text if block_given?
      elsif event.delta.type == :input_json_delta
        # Accumulate tool input JSON fragments
        tool_use_content_json += event.delta.partial_json || ""
      end
    when :content_block_start
      if event.content_block.type == :tool_use
        tool_use = event.content_block
      end
    end
  end

  if tool_use.to_hash.any?
    {
      "role" => "assistant",
      content: [tool_use.to_hash.merge({input: JSON.parse(tool_use_content_json)})]
    }
  else
    {
      "role" => "assistant",
      "content" => full_content
    }
  end
end