100 lines
3.3 KiB
Python
100 lines
3.3 KiB
Python
"""Tests for the `options=` parameter on `.prompt()` and `.reply()`.
|
|
|
|
The `options={...}` dict form is the documented API; the `**kwargs` form
|
|
continues to work undocumented for backwards compatibility.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
def test_prompt_with_options_dict(mock_model):
|
|
mock_model.enqueue(["ok"])
|
|
r = mock_model.prompt("q", options={"max_tokens": 42})
|
|
r.text()
|
|
assert r.prompt.options.max_tokens == 42
|
|
assert r.to_dict()["prompt"].get("options") == {"max_tokens": 42}
|
|
|
|
|
|
def test_prompt_kwargs_still_work(mock_model):
|
|
mock_model.enqueue(["ok"])
|
|
r = mock_model.prompt("q", max_tokens=42)
|
|
r.text()
|
|
assert r.prompt.options.max_tokens == 42
|
|
|
|
|
|
def test_prompt_options_and_kwargs_merge(mock_model):
|
|
# Non-overlapping keys merge cleanly — options= and kwargs both contribute
|
|
mock_model.Options.model_rebuild()
|
|
mock_model.enqueue(["ok"])
|
|
# Only max_tokens exists on MockModel.Options — use it via options=.
|
|
# Pass an empty options dict alongside a kwarg to confirm both paths coexist.
|
|
r = mock_model.prompt("q", options={}, max_tokens=7)
|
|
r.text()
|
|
assert r.prompt.options.max_tokens == 7
|
|
|
|
|
|
def test_prompt_options_and_kwargs_conflict_raises(mock_model):
|
|
mock_model.enqueue(["ok"])
|
|
with pytest.raises(TypeError, match="both in options="):
|
|
mock_model.prompt("q", options={"max_tokens": 1}, max_tokens=2)
|
|
|
|
|
|
def test_conversation_prompt_with_options_dict(mock_model):
|
|
mock_model.enqueue(["ok"])
|
|
convo = mock_model.conversation()
|
|
r = convo.prompt("q", options={"max_tokens": 99})
|
|
r.text()
|
|
assert r.prompt.options.max_tokens == 99
|
|
|
|
|
|
def test_response_reply_with_options_dict(mock_model):
|
|
mock_model.enqueue(["first"])
|
|
mock_model.enqueue(["second"])
|
|
r1 = mock_model.prompt("q1", options={"max_tokens": 5})
|
|
r1.text()
|
|
r2 = r1.reply("q2", options={"max_tokens": 17})
|
|
r2.text()
|
|
assert r2.prompt.options.max_tokens == 17
|
|
|
|
|
|
def test_response_reply_kwargs_still_work(mock_model):
|
|
mock_model.enqueue(["first"])
|
|
mock_model.enqueue(["second"])
|
|
r1 = mock_model.prompt("q1", max_tokens=5)
|
|
r1.text()
|
|
r2 = r1.reply("q2", max_tokens=17)
|
|
r2.text()
|
|
assert r2.prompt.options.max_tokens == 17
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_prompt_with_options_dict(async_mock_model):
|
|
# AsyncMockModel inherits the empty base Options (extra="forbid"),
|
|
# so pass an empty options dict — this verifies the parameter is
|
|
# accepted and the empty-dict path works.
|
|
async_mock_model.enqueue(["ok"])
|
|
r = await async_mock_model.prompt("q", options={}).text()
|
|
assert r == "ok"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_prompt_options_and_kwargs_conflict_raises(async_mock_model):
|
|
import llm
|
|
|
|
# Build an async model with a real Option field so we can collide them.
|
|
class AsyncModelWithOption(llm.AsyncModel):
|
|
model_id = "async-with-option"
|
|
|
|
class Options(llm.Options):
|
|
from typing import Optional as _Opt
|
|
from pydantic import Field as _Field
|
|
|
|
max_tokens: _Opt[int] = _Field(default=None)
|
|
|
|
async def execute(self, prompt, stream, response, conversation):
|
|
yield "ok"
|
|
|
|
m = AsyncModelWithOption()
|
|
with pytest.raises(TypeError, match="both in options="):
|
|
await m.prompt("q", options={"max_tokens": 1}, max_tokens=2).text()
|