Serve

crowe serve runs the agent as an OpenAI-compatible HTTP API on your machine. Any OpenAI client, the official SDK, LangChain, Cortex, can drive the same agent loop the console uses by pointing its base URL at the bridge. Each request becomes one headless turn; the bridge translates the request in and the event stream out.

Start it

$ crowe serve
Crowe Logic OpenAI bridge → http://127.0.0.1:8011/v1  (Ctrl+C to stop)
INFO:     Uvicorn running on http://127.0.0.1:8011 (Press CTRL+C to quit)
FlagMeaning
--host TEXTBind host. Default 127.0.0.1, or CROWE_BRIDGE_HOST.
--port INTEGERBind port. Default 8011, or CROWE_BRIDGE_PORT.

The bridge does not check an API key. Clients require one, so pass any string. Keep it bound to loopback unless you put your own authentication in front of it.

Endpoints

RouteReturns
GET /healthz{"status":"ok","service":"crowe-logic-bridge"}
GET /v1/modelsAn OpenAI model list: every selector from the model table except CroweLM Auto, each owned_by: crowe-logic.
POST /v1/chat/completionsA chat completion, streamed or whole.
$ curl -s http://127.0.0.1:8011/v1/models
{"object": "list", "data": [
  {"id": "crowelm", "object": "model", "created": 1788249158, "owned_by": "crowe-logic"},
  {"id": "crowelm-high", "object": "model", "created": 1788249158, "owned_by": "crowe-logic"},
  {"id": "crowelm-max", "object": "model", "created": 1788249158, "owned_by": "crowe-logic"},
  {"id": "crowelm-zenith", "object": "model", "created": 1788249158, "owned_by": "crowe-logic"},
  ...
]}

The request

The bridge reads four fields of the chat completions body and ignores the rest; each tier's own runtime parameters apply, so temperature, max_tokens and client-side tools have no effect.

curl

curl -s http://127.0.0.1:8011/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "crowelm-flash",
    "messages": [{"role": "user", "content": "Name the engine you run on."}]
  }'

The answer is a chat.completion object with one choice and finish_reason: stop. Usage counts are approximate: prompt_tokens is 0 and completion_tokens is a word count of the answer.

{"id": "chatcmpl-...", "object": "chat.completion", "created": 1788249158, "model": "crowelm-flash",
 "choices": [{"index": 0, "message": {"role": "assistant", "content": "..."}, "finish_reason": "stop"}],
 "usage": {"prompt_tokens": 0, "completion_tokens": N, "total_tokens": N}}

With "stream": true the response is text/event-stream: chat.completion.chunk objects with delta.content, opened by one chunk with empty content as the role marker, closed by a chunk with finish_reason: stop and then data: [DONE]. A tool call the agent makes appears inline in the content as _[tool: name ok]_; reasoning deltas are not forwarded. An error mid-stream arrives as content of the form [error: message] followed by the stop chunk.

Python, with the OpenAI client

from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:8011/v1", api_key="not-checked")

reply = client.chat.completions.create(
    model="crowelm",
    messages=[{"role": "user", "content": "Name the engine you run on."}],
)
print(reply.choices[0].message.content)

for chunk in client.chat.completions.create(
    model="crowelm-zenith",
    messages=[{"role": "user", "content": "Outline a retry policy for a flaky upstream."}],
    stream=True,
):
    print(chunk.choices[0].delta.content or "", end="", flush=True)

Errors

400 when messages is missing, empty, or does not end with a user turn. 503 when the turn could not find a runtime; the detail reads: "This bridge runs the model on your own provider keys or a Crowe ID sign-in, and found neither. Put keys in .env or run crowe login; the free tier is available in crowe chat." 500 for any other provider error, with the headless error message as the detail.