Closed simonw closed 1 week ago
Surprisingly this new test passes:
To paste into a python -m asyncio
shell to try it out:
import llm
model = llm.get_async_model("gpt-4o-mini")
c = model.conversation()
print(await c.prompt('two jokes about a duck'))
print(await c.prompt("walrus"))
I dropped into the debugger and found the problem:
> /Users/simon/Dropbox/Development/llm/llm/default_plugins/openai_models.py(495)execute()
-> completion = await client.chat.completions.create(
(Pdb) list
490 raise NotImplementedError("Model does not support system prompts")
491 messages = self.build_messages(prompt, conversation)
492 kwargs = self.build_kwargs(prompt, stream)
493 client = self.get_client(async_=True)
494 if stream:
495 -> completion = await client.chat.completions.create(
496 model=self.model_name or self.model_id,
497 messages=messages,
498 stream=True,
499 **kwargs,
500 )
(Pdb) messages
[{'role': 'user', 'content': 'two jokes about a duck'}, {'role': 'assistant', 'content': <coroutine object AsyncResponse.text at 0x309e4dd90>}, {'role': 'user', 'content': 'walrus'}]
It's here:
I'm calling response.text()
without await
there to try and reconstruct the messages
array.
So that build_messages()
method can't be shared between the sync and async worlds, because it needs to be an async def build_messages()
method in async land.
But it has a bunch of logic that I'd rather not duplicate.
Could we assume that response
has been previously awaited (and hence the text made available) by the time it's passed to that method, and raise an error if it has NOT? That might work.
diff --git a/llm/default_plugins/openai_models.py b/llm/default_plugins/openai_models.py
index 82f737c..3d9816e 100644
--- a/llm/default_plugins/openai_models.py
+++ b/llm/default_plugins/openai_models.py
@@ -375,7 +375,7 @@ class _Shared:
messages.append(
{"role": "user", "content": prev_response.prompt.prompt}
)
- messages.append({"role": "assistant", "content": prev_response.text()})
+ messages.append({"role": "assistant", "content": prev_response.text_or_raise()})
if prompt.system and prompt.system != current_system:
messages.append({"role": "system", "content": prompt.system})
if not prompt.attachments:
diff --git a/llm/models.py b/llm/models.py
index cb9c7ab..7b61411 100644
--- a/llm/models.py
+++ b/llm/models.py
@@ -393,6 +393,11 @@ class AsyncResponse(_BaseResponse):
pass
return self
+ def text_or_raise(self) -> str:
+ if not self._done:
+ raise ValueError("Response not yet awaited")
+ return "".join(self._chunks)
+
async def text(self) -> str:
await self._force()
return "".join(self._chunks)
That does seem to fix it.