# LlmClient.Complete — a model's answer, once

> `LlmClient.Complete(prompt)` asks a model a question and gives you the finished text. Use it when your code wants the answer as a value — to store it, branch on it, or pass it on — and nobody is watching it being written.

<!-- id: function-llm-complete · area: function · stability: preview · html: https://osysharp.com/reference/function/llm-complete/ -->

## Summary        {#summary}
`LlmClient.Complete(prompt)` is the plain model call: text in, text out. It waits for the whole answer and hands it
back as a `string`, so the value can be stored on a record, tested in an `if`, or passed to anything else.

If somebody is *watching* the answer appear, you want [LlmClient.Stream — a model's answer as it is written](https://osysharp.com/reference/function/llm-stream/) instead.

## Signature      {#signature}
```osy syntax
string LlmClient.Complete(string prompt)
string LlmClient.Complete(string prompt, string model)
```

## Description    {#description}
The call returns when the model has finished. That is the whole difference from [LlmClient.Stream — a model's answer as it is written](https://osysharp.com/reference/function/llm-stream/): same model,
same prompt, same cost — the answer simply arrives as one value instead of in pieces.

Reach for `Complete` when the answer is **data your code acts on**, and for `Stream` when the answer is **something a
person reads**. Classifying a support ticket, drafting a field to save, summarising a document into a column — those
want the finished string, and nothing about them is improved by seeing it typed out.

**What it does not do:**
- **No tools, no system prompt, no usage numbers.** This is the raw completion. A richer surface — one that can call
  tools and carry a conversation — belongs with agents.
- **It needs a model provider.** A host that has wired none fails the call rather than returning an empty string: an
  empty answer and a missing provider must not look the same.
- **It does not stream.** A long answer means a long wait, with nothing to show for it until it lands. That is the
  trade, and it is the right one only when nobody is waiting on a screen.

### Choosing the model        {#model}
An app declares its model once (`app.DefaultModel`), and every call uses it. A call that needs a *different* one can
say so:

```osy syntax
string verdict = LlmClient.Complete(ticket, "claude-opus-4-8");
```

⚠ **Naming a model can only NARROW what the app allows.** The name is matched against the models the app admits; if
it is not one of them the call is **refused**, with a message naming what was asked for and what the app allows. It
is never quietly served by the default — a call site cannot know the app's policy, which is exactly why relaxing it
is not the call site's to do. This is the same rule [LlmClient.Stream — a model's answer as it is written](https://osysharp.com/reference/function/llm-stream/) follows, deliberately: which model serves
a call is not a thing you should have to think about differently depending on how you read the answer.

### What does a call cost, and what if I am over budget?        {#cost}
Every call is metered before it is made and recorded after: an app that is over its budget is refused rather than
served, and what the call actually spent is written down against the app.

## Examples       {#examples}
Classifying a ticket and saving the answer:

```osy test app=function-llm-complete-triage
entity Ticket {
  [MaxLength(200)] string Subject;
  [MaxLength(4000)] string Body;
  [MaxLength(40)] string Category;
}

void Triage(Ticket ticket) {
  ticket.Category = LlmClient.Complete(
    "Reply with one word — billing, technical or other. Ticket: " + ticket.Subject);
}
```

## See also       {#see-also}
- [LlmClient.Stream — a model's answer as it is written](https://osysharp.com/reference/function/llm-stream/) — the same call, delivered as it is written
- [function](https://osysharp.com/reference/function/declaration/) — where a server function runs
