@@ -37,11 +37,28 @@ def model_uri
3737 URI ( llm_model . url )
3838 end
3939
40- def prepare_payload ( prompt , model_params , _dialect )
40+ def native_tool_support?
41+ @native_tool_support
42+ end
43+
44+ def has_tool? ( _response_data )
45+ @has_function_call
46+ end
47+
48+ def prepare_payload ( prompt , model_params , dialect )
49+ @native_tool_support = dialect . native_tool_support?
50+
51+ # https://github.com/ollama/ollama/blob/main/docs/api.md#parameters-1
52+ # Due to ollama enforce a 'stream: false' for tool calls, instead of complicating the code,
53+ # we will just disable streaming for all ollama calls if native tool support is enabled
54+
4155 default_options
4256 . merge ( model_params )
4357 . merge ( messages : prompt )
44- . tap { |payload | payload [ :stream ] = false if !@streaming_mode }
58+ . tap { |payload | payload [ :stream ] = false if @native_tool_support || !@streaming_mode }
59+ . tap do |payload |
60+ payload [ :tools ] = dialect . tools if @native_tool_support && dialect . tools . present?
61+ end
4562 end
4663
4764 def prepare_request ( payload )
@@ -58,7 +75,66 @@ def extract_completion_from(response_raw)
5875 parsed = JSON . parse ( response_raw , symbolize_names : true )
5976 return if !parsed
6077
61- parsed . dig ( :message , :content )
78+ response_h = parsed . dig ( :message )
79+
80+ @has_function_call ||= response_h . dig ( :tool_calls ) . present?
81+ @has_function_call ? response_h . dig ( :tool_calls , 0 ) : response_h . dig ( :content )
82+ end
83+
84+ def add_to_function_buffer ( function_buffer , payload : nil , partial : nil )
85+ @args_buffer ||= +""
86+
87+ if @streaming_mode
88+ return function_buffer if !partial
89+ else
90+ partial = payload
91+ end
92+
93+ f_name = partial . dig ( :function , :name )
94+
95+ @current_function ||= function_buffer . at ( "invoke" )
96+
97+ if f_name
98+ current_name = function_buffer . at ( "tool_name" ) . content
99+
100+ if current_name . blank?
101+ # first call
102+ else
103+ # we have a previous function, so we need to add a noop
104+ @args_buffer = +""
105+ @current_function =
106+ function_buffer . at ( "function_calls" ) . add_child (
107+ Nokogiri ::HTML5 ::DocumentFragment . parse ( noop_function_call_text + "\n " ) ,
108+ )
109+ end
110+ end
111+
112+ @current_function . at ( "tool_name" ) . content = f_name if f_name
113+ @current_function . at ( "tool_id" ) . content = partial [ :id ] if partial [ :id ]
114+
115+ args = partial . dig ( :function , :arguments )
116+
117+ # allow for SPACE within arguments
118+ if args && args != ""
119+ @args_buffer << args . to_json
120+
121+ begin
122+ json_args = JSON . parse ( @args_buffer , symbolize_names : true )
123+
124+ argument_fragments =
125+ json_args . reduce ( +"" ) do |memo , ( arg_name , value ) |
126+ memo << "\n <#{ arg_name } >#{ value } </#{ arg_name } >"
127+ end
128+ argument_fragments << "\n "
129+
130+ @current_function . at ( "parameters" ) . children =
131+ Nokogiri ::HTML5 ::DocumentFragment . parse ( argument_fragments )
132+ rescue JSON ::ParserError
133+ return function_buffer
134+ end
135+ end
136+
137+ function_buffer
62138 end
63139 end
64140 end
0 commit comments