crush icon indicating copy to clipboard operation
crush copied to clipboard

Support Mistral API endpoints

Open ujh opened this issue 4 months ago β€’ 7 comments

I tried to configure a custom provider to connect to the Mistral API like so:

{
  "providers": {
    "mistral": {
      "name": "Mistral",
      "base_url": "https://api.mistral.ai/v1/",
      "type": "openai",
      "api_key": "...",
      "models": [
        {
          "name": "Devstral Medium",
          "id": "devstral-medium-latest",
          "context_window": 128000,
          "default_max_tokens": 20000
        }
      ]
    }
  }
}

However, when I end up with a 422 when the request is being made. Digging into the logs, here's an example request:

{"time":"2025-08-08T16:21:40.041695+02:00","level":"DEBUG","source":{"function":"github.com/charmbracelet/crush/internal/log.(*HTTPRoundTripLogger).RoundTrip","file":"/home/runner/work/crush/crush/internal/log/http.go","line":46},"msg":"HTTP Request","method":"POST","url":{"Scheme":"https","Opaque":"","User":null,"Host":"api.mistral.ai","Path":"/v1/chat/completions","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"body":"{\"messages\":[{\"content\":\"you will generate a short title based on the first message a user begins a conversation with\\n\\n- ensure it is not more than 50 characters long\\n- the title should be a summary of the user's message\\n- it should be one line long\\n- do not use quotes or colons\\n- the entire text you return will be used as the title\\n- never return anything that is more than one sentence (one line) long\\n\",\"role\":\"system\"},{\"content\":\"Generate a concise title for the following content:\\n\\n`Please analyze this codebase and create a **CRUSH.md** file containing:\\n\\n- Build/lint/test commands - especially for running a single test\\n- Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.\\n\\nThe file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20-30 lines long.\\nIf there's already a **CRUSH.md**, improve it.\\n\\nIf there are Cursor rules (in `.cursor/rules/` or `.cursorrules`) or Copilot rules (in `.github/copilot-instructions.md`), make sure to include them.\\nAdd the `.crush` directory to the `.gitignore` file if it's not already there.\\n\",\"role\":\"user\"}],\"model\":\"devstral-medium-latest\",\"max_tokens\":20000,\"stream_options\":{\"include_usage\":true},\"stream\":true}"}

And this is the error I get:

{"time":"2025-08-08T16:21:40.195155+02:00","level":"DEBUG","source":{"function":"github.com/charmbracelet/crush/internal/log.(*HTTPRoundTripLogger).RoundTrip","file":"/home/runner/work/crush/crush/internal/log/http.go","line":68},"msg":"HTTP Response","status_code":422,"status":"422 Unprocessable Entity","headers":{"Access-Control-Allow-Origin":["*"],"Alt-Svc":["h3=\":443\"; ma=86400"],"Cf-Cache-Status":["DYNAMIC"],"Cf-Ray":["96bfa9559e31085c-FRA"],"Content-Length":["224"],"Content-Type":["application/json"],"Date":["Fri, 08 Aug 2025 14:21:40 GMT"],"Mistral-Correlation-Id":["01988a0f-119a-7eac-80cb-e608f2264854"],"Server":["cloudflare"],"Set-Cookie":["__cf_bm=922.okTGThgZn6EE11Oj4g05wI9GrzTpyOpfRB3nfO0-1754662900-1.0.1.1-WaDFfZ2lp0TCq88WIMAc6rE.wr_khUR5TWZ4kY4JifiZ_LMzqS1gsD_g3pd48TOicDSiNALfawnHN1kM6bITSTG.K3CCtr14DONF_smUxl8; path=/; expires=Fri, 08-Aug-25 14:51:40 GMT; domain=.mistral.ai; HttpOnly; Secure; SameSite=None","_cfuvid=ZG5l0SQAontyjfB84i6fCi2v6C.CyFH3n_6aKiRCrB4-1754662900182-0.0.1.1-604800000; path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None"],"Strict-Transport-Security":["max-age=15552000; includeSubDomains"],"X-Envoy-Upstream-Service-Time":["28"],"X-Kong-Proxy-Latency":["7"],"X-Kong-Request-Id":["01988a0f-119a-7eac-80cb-e608f2264854"],"X-Kong-Upstream-Latency":["29"]},"body":"{\"object\":\"error\",\"message\":{\"detail\":[{\"type\":\"extra_forbidden\",\"loc\":[\"body\",\"stream_options\",\"include_usage\"],\"msg\":\"Extra inputs are not permitted\",\"input\":true}]},\"type\":\"invalid_request_error\",\"param\":null,\"code\":null}","content_length":224,"duration_ms":153,"error":null}

It seems to me that the stream_options part of the request is not accepted.

Is there a way for me to test this some more apart from actually doing this in the code or am I maybe doing something wrong in some other place?

ujh avatar Aug 08 '25 14:08 ujh

The API docs also don't include stream_options, so it seems to make sense.

ujh avatar Aug 08 '25 15:08 ujh

Yeah, that’s the only thing holding me back from giving this tool a try. Kinda disappointing.

sigmanor avatar Aug 26 '25 19:08 sigmanor

Hey all, I was just able to successfully use this with my own API Key today:

 β”‚ Hi!                               
                                     
   Hello! How can I assist you today?
                                     
   β—‡ Devstral Medium 3s ─────────────

Could you give it another try? (I'm running on HEAD of main for what its worth)

dmizelle avatar Oct 22 '25 18:10 dmizelle

It didn't work for me on Crush v0.13.7.

I just tried Crush v0.18.1, and it seems to work now.

yookoala avatar Nov 14 '25 10:11 yookoala

@ujh: Have you tried the latest stable Crush with your setting?

If it works now, then this issue is no longer relevant and can be closed.

yookoala avatar Nov 14 '25 15:11 yookoala

tested with crush 0.18.6, it works, but somewhat buggy.

Often after prompting there is no response. Then can prompt a few more times, with different words, or different questions, and it comes back with answer for all the previous promts too.

ckjoris avatar Nov 25 '25 10:11 ckjoris

Switching back from 0.18.6 to 0.18.1, I'm seeing the same behavior as @ckjoris . I'm going to see if I have any time over the holiday weekend (like 50/50 chance) and see if I can figure something out.

dmizelle avatar Nov 25 '25 19:11 dmizelle