gemlings 0.3.1 → 0.3.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe9ef70286e116937fc896cdc9d23b3ac6ea88b5d071c5055355664784fe9ecc
4
- data.tar.gz: 724554f2afc3d3d71862673ea44388ab89c70568ad609b4de32a0d4fe3e3d59b
3
+ metadata.gz: 9e1ddaa59fb419c9b79901c5bc307303a2fe7b8f9d2860918d2cda34a95b917f
4
+ data.tar.gz: c136329db04947954e039bdd139081572aaae1c66a08c3196eb63fc36d453448
5
5
  SHA512:
6
- metadata.gz: 0d2f461b85343d4617626aece17873b5d2d1c60c5cf79303abac0b9276f0764b77cdf21bed385139dfa7fc094f5fe9402ad408cbbae7059ea12507bdefce15b9
7
- data.tar.gz: f3289f4a29b87a9ab296d47f2a441e0699e5702839d8bdc33111d8efc36c4072417d12d16638efba7d0d32675a5fac1ce56b2f6dc5c0ba18906b2dd04a906c9a
6
+ metadata.gz: d45d3793c999be51f362b868ef95039da9dadd7e52a1fb8199508a14553b0a07974146a99c652adc357a7581d997febb1639415eb3fb3bf9dbea8e97c49c868e
7
+ data.tar.gz: 6a0daa95cf7d74009dca1857b0f44036d663efb23cb3e8187ec0d5888b4463640634b9e8edb8f8bd493d955b770232e6c665b253986cbe72996801d906490324
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.2
4
+
5
+ Bug fixes and Ollama improvements.
6
+
7
+ - **Fix Anthropic tool_result format** -- ToolCallingAgent now emits structured `tool_result` blocks instead of plain text observations, fixing 400 errors on multi-step runs with Anthropic models (thanks @parolkar)
8
+ - **Fix trailing whitespace in messages** -- Strip trailing whitespace from message content to avoid Anthropic API rejections
9
+ - **Fix Ollama connectivity** -- Default `ollama_api_base` to `http://localhost:11434/v1` so Ollama works out of the box without setting `OLLAMA_HOST`
10
+ - **Fix MCP transport leak** -- Close MCP transport on error paths (no tools found, tool name not found)
11
+ - **Fix `planning_interval: 0`** -- Guard against `ZeroDivisionError` when planning interval is zero
12
+
3
13
  ## 0.3.0
4
14
 
5
15
  JRuby 10 support, interactive UI, and CI.
@@ -102,7 +102,18 @@ module Gemlings
102
102
  messages << assistant_msg if assistant_msg
103
103
 
104
104
  if step.observation
105
- messages << { role: "user", content: "Observation: #{step.observation}" }
105
+ if step.tool_calls&.any?
106
+ # Some LLM APIs (e.g. Anthropic) require that every tool_use block in an
107
+ # assistant message is immediately followed by a tool_result block in the
108
+ # next user message — a plain text "Observation:" message is rejected.
109
+ # Build a structured tool_result entry for each tool call in this step.
110
+ tool_results = step.tool_calls.map do |tc|
111
+ { type: "tool_result", tool_use_id: tc.id, content: step.observation }
112
+ end
113
+ messages << { role: "user", content: tool_results }
114
+ else
115
+ messages << { role: "user", content: "Observation: #{step.observation}" }
116
+ end
106
117
  elsif step.error
107
118
  messages << {
108
119
  role: "user",
@@ -113,7 +124,12 @@ module Gemlings
113
124
  end
114
125
  end
115
126
 
116
- messages.each { |m| m[:content] = sanitize_utf8(m[:content]) if m[:content] }
127
+ # Some LLM APIs (e.g. Anthropic) reject messages whose string content ends with
128
+ # trailing whitespace. Strip it from every message to avoid API errors.
129
+ messages.each do |m|
130
+ m[:content] = sanitize_utf8(m[:content]) if m[:content].is_a?(String)
131
+ m[:content] = m[:content].rstrip if m[:content].is_a?(String)
132
+ end
117
133
  end
118
134
 
119
135
  def last_step
@@ -44,7 +44,8 @@ module Gemlings
44
44
  config.gemini_api_key = ENV["GEMINI_API_KEY"] if ENV["GEMINI_API_KEY"]
45
45
  config.deepseek_api_key = ENV["DEEPSEEK_API_KEY"] if ENV["DEEPSEEK_API_KEY"]
46
46
  config.openrouter_api_key = ENV["OPENROUTER_API_KEY"] if ENV["OPENROUTER_API_KEY"]
47
- config.ollama_api_base = ENV["OLLAMA_HOST"] if ENV["OLLAMA_HOST"]
47
+ ollama_base = ENV["OLLAMA_HOST"] || "http://localhost:11434"
48
+ config.ollama_api_base = "#{ollama_base.chomp("/")}/v1"
48
49
  end
49
50
 
50
51
  @configured = true
@@ -58,7 +59,7 @@ module Gemlings
58
59
 
59
60
  def load_messages(chat, messages)
60
61
  messages.each do |msg|
61
- role = msg[:role]
62
+ role = msg[:role]
62
63
  content = msg[:content]
63
64
 
64
65
  case role
@@ -71,11 +72,30 @@ module Gemlings
71
72
  end
72
73
  chat.add_message(attrs)
73
74
  else
74
- chat.add_message(role: role.to_sym, content: content || "")
75
+ # A user message whose content is an array of tool_result hashes is the
76
+ # structured response to one or more tool_use calls in the preceding
77
+ # assistant message. ruby_llm expects these to be added as separate
78
+ # messages with role: :tool and a tool_call_id, not as plain user text.
79
+ if tool_result_content?(content)
80
+ content.each do |tr|
81
+ chat.add_message(
82
+ role: :tool,
83
+ content: (tr[:content] || tr["content"]).to_s,
84
+ tool_call_id: tr[:tool_use_id] || tr["tool_use_id"]
85
+ )
86
+ end
87
+ else
88
+ chat.add_message(role: role.to_sym, content: content || "")
89
+ end
75
90
  end
76
91
  end
77
92
  end
78
93
 
94
+ def tool_result_content?(content)
95
+ content.is_a?(Array) &&
96
+ content.all? { |c| c.is_a?(Hash) && (c[:type] == "tool_result" || c["type"] == "tool_result") }
97
+ end
98
+
79
99
  def load_tools(chat, tool_schemas)
80
100
  tool_schemas.each { |schema| chat.with_tool(build_stub_tool(schema)) }
81
101
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gemlings
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemlings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hasiński