apexive / apexive/odoo-llm

llm_ollama broken after llm base refactors: role helpers used as bool (all messages become "user"), format_messages signature, arguments type

Ouverte
#262 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Python
Étoiles
219
Forks
159
Métriques de merge des PR
Aucune PR mergée en 30 j

Description

**Environment:** branch `18.0` @ `609ec6dda3645165f6a4a843d7af5d286081a29d`, Odoo 18 (official docker image), python `ollama` client 0.6.2, models tested: qwen3:8b, qwen2.5:7b.

With this combination, any `llm.thread` chat using tools via the Ollama provider never converges: the model keeps re-issuing the same tool call (threads grow by hundreds of messages) or asks the user for parameters it already used. We tracked it down to three independent regressions plus one unenforced field. Details below — happy to send a PR with the fixes if useful.

### 1. `llm_ollama` formats EVERY message as role "user" → tool results never reach the model

`llm/models/mail_message.py` refactored the role helpers to a batch API returning a dict:

```python
def is_llm_user_message(self):
return {message: message.llm_role == "user" for message in self}
```

`llm_openai`, `llm_anthropic` and `llm_letta` were updated to the new call form (`self.is_llm_user_message()[self]`), but `llm_ollama/models/mail_message.py::ollama_format_message` still uses the old boolean form:

```python
if self.is_llm_user_message(): # non-empty dict -> always truthy
```

A non-empty dict is always truthy, so EVERY message in the history is formatted as `{"role": "user", "content": }`. Assistant `tool_calls` and tool results are silently dropped from the payload — the model literally sees a conversation made only of "user" messages like `Executing consultar_estoque_produto`.

Fix (mirrors `llm_openai`):

```python
if self.is_llm_user_message()[self]:
...
elif self.is_llm_assistant_message()[self]:
```

### 2. `format_messages` dispatch signature mismatch

`llm/models/llm_provider.py::format_messages` dispatches with `model=model`, but `ollama_format_messages(self, messages, system_prompt=None)` does not accept it → `TypeError` on every thread+ollama chat. Fix: add `model=None` to the signature.

### 3. History resend rejected: `arguments` serialized as JSON string, ollama client >=0.4 requires dict

`ollama_process_(non_)streaming_response` stores `function.arguments` as a JSON **string** (`json.dumps(arguments)`), and `ollama_format_message` sends it back verbatim when re-sending the history. The python `ollama` client validates `Message.tool_calls[].function.arguments` as a **dict** (pydantic), so the second chat call of every tool conversation fails client-side:

```
1 validation error for Message tool_calls.0.function.arguments
Input should be a valid dictionary [type=dict_type, input_value='{"etapa": ""}', input_type=str]
```

Fix in `ollama_format_message` (assistant branch): `json.loads(...)` when the stored value is a string.

### 4. `llm_assistant.tool_calls_max` is never read

The field exists on `llm.assistant` (and is copied to threads via onchange), but no code enforces it, so nothing bounds the tool loop — which turns bug 1 into an unbounded loop (we observed 300+ messages from a single question). Suggest enforcing it in `llm_thread.generate_messages`/`_should_continue`.

---

After patching 1–3 locally (and enforcing 4 in a custom module), tool calling with Ollama works end to end: correct roles in the payload, tool results fed back to the model, and grounded answers.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.