0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-08 17:53:55 +08:00
discourse/plugins/discourse-ai/lib/agents/tool_runner/llm.rb
Sam 834324206e
FEATURE: add AI agent thinking effort (#41280)
Add a per-agent thinking_effort setting that can be edited in the admin
UI, serialized, imported, exported, and passed through agent
completions.

Normalize provider-agnostic effort values for supported LLM backends,
including Anthropic, Gemini, OpenAI, OpenRouter, vLLM, and Bedrock,
while preserving legacy provider parameters and reserving output tokens
where required.

---------

Co-authored-by: Rafael Silva <xfalcox@gmail.com>
2026-07-01 17:03:19 -03:00

73 lines
2.4 KiB
Ruby
Vendored

# frozen_string_literal: true
module DiscourseAi
module Agents
class ToolRunner
module Llm
def attach_truncate(mini_racer_context)
mini_racer_context.attach(
"_llm_truncate",
->(text, length) do
@llm.tokenizer.truncate(text, length, strict: SiteSetting.ai_strict_token_counting)
end,
)
mini_racer_context.attach(
"_llm_generate",
->(prompt, options) do
in_attached_function do
options ||= {}
response_format = options["response_format"]
response_format = { "type" => "json_object" } if options["json"]
if response_format && !response_format.is_a?(Hash)
raise ::Discourse::InvalidParameters.new("response_format must be a hash")
end
@llm.generate(
convert_js_prompt_to_ruby(prompt),
user: llm_user,
feature_name: "custom_tool_#{tool.name}",
response_format: response_format,
temperature: options["temperature"],
top_p: options["top_p"],
max_tokens: options["max_tokens"],
thinking_effort: options["thinking_effort"],
stop_sequences: options["stop_sequences"],
)
end
end,
)
end
private
def convert_js_prompt_to_ruby(prompt)
if prompt.is_a?(String)
prompt
elsif prompt.is_a?(Hash)
messages = prompt["messages"]
if messages.blank? || !messages.is_a?(Array)
raise ::Discourse::InvalidParameters.new("Prompt must have messages")
end
messages.each(&:symbolize_keys!)
messages.each { |message| message[:type] = message[:type].to_sym }
DiscourseAi::Completions::Prompt.new(messages: prompt["messages"])
else
raise ::Discourse::InvalidParameters.new("Prompt must be a string or a hash")
end
end
def llm_user
@llm_user ||= post&.user || @bot_user
end
def post
return @post if defined?(@post)
post_id = @context.post_id
@post = post_id && Post.find_by(id: post_id)
end
end
end
end
end