Skip to content

conversations.run_async silently drops typed input entries such as MessageInputEntry #640

Open

Description

@sebxt

What happens

client.beta.conversations.run_async and run_stream_async accept inputs as a string or as a list of input entries (ConversationInputs). When the list contains SDK model instances, only FunctionResultEntry objects are kept. Every other entry type (MessageInputEntry, MessageOutputEntry, FunctionCallEntry, ToolExecutionEntry, AgentHandoffEntry) is discarded without any error, so the request is sent with those entries missing. With a single MessageInputEntry the conversation is started with inputs: [].

Dicts with the same content work, which makes this easy to miss.

What should happen

Typed entries should be forwarded exactly like their dict equivalents, in the order given.

Minimal repro

import asyncio, json
import httpx2 as httpx
from mistralai.client import Mistral
from mistralai.client.models import MessageInputEntry
from mistralai.extra.run.context import RunContext

sent = []

def handler(request):
    sent.append(json.loads(request.content))
    return httpx.Response(200, json={
        "object": "conversation.response", "conversation_id": "conv_1",
        "outputs": [{"object": "entry", "type": "message.output", "role": "assistant", "content": "ok"}],
        "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
    })

async def main():
    client = Mistral(api_key="test", async_client=httpx.AsyncClient(transport=httpx.MockTransport(handler)))
    async with RunContext(model="mistral-medium-latest") as ctx:
        await client.beta.conversations.run_async(
            run_ctx=ctx, inputs=[MessageInputEntry(role="user", content="Hello")]
        )
    print(sent[0]["inputs"])

asyncio.run(main())

Output on main: []

Expected: [{'role': 'user', 'content': 'Hello', 'object': 'entry', 'type': 'message.input', 'prefix': False}]

Root cause

_validate_run in src/mistralai/extra/run/context.py builds the input list like this:

for input in inputs:
    if isinstance(input, dict):
        input_entries.append(pydantic.TypeAdapter(InputEntries).validate_python(input))
    elif isinstance(input, FunctionResultEntry):
        input_entries.append(input)

There is no branch for the other members of InputEntries, so they fall through and are dropped. The same list is also stored as RunResult.input_entries, so run_result.entries is incomplete as well.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions