MCPcopy Create free account
hub / github.com/codehamr/codehamr / run

Method run

internal/llm/llm.go:292–338  ·  view source on GitHub ↗
(parent context.Context, msgs []chmctx.Message, tools []Tool, out chan<- Event)

Source from the content-addressed store, hash-verified

290 ContextWindow int
291 Budget cloud.BudgetStatus
292}
293
294// Probe sends a minimal hello chat just to harvest response headers in one
295// round trip: status validates the URL/model/key combo, X-Context-Window gives
296// the live size, X-Budget-Remaining the live fraction. The body is closed
297// unread; on the cloud proxy that may already charge one token, the cost of a
298// single round-trip "key works AND here is your real window". Returns the
299// standard cloud errors (Unreachable, Unauthorized, BudgetExhausted) for
300// errors.Is branching.
301func (c *Client) Probe(parent context.Context) (ProbeResult, error) {
302 resp, budget, err := c.post(parent, request{
303 Model: c.Model,
304 Input: []any{messageItem{Type: "message", Role: "user", Content: "hi"}},
305 Stream: true,
306 })
307 if err != nil {
308 return ProbeResult{Budget: budget}, err
309 }
310 defer resp.Body.Close()
311 return ProbeResult{
312 ContextWindow: cloud.ContextWindowFromHeaders(resp.Header),
313 Budget: cloud.FromHeaders(resp.Header),
314 }, nil
315}
316
317// Chat streams an assistant response on the returned channel, closing it when
318// the stream ends. Reasoning runs at `medium` effort: decode is the serialised
319// critical path of every round, and `high` bought deliberation the agent loop
320// already gets from seeing each tool result. If the server rejects the effort
321// (see rejectsReasoning), post drops it for this Client's lifetime so the model
322// still works at the server's own default.
323func (c *Client) Chat(parent context.Context, messages []chmctx.Message, tools []Tool) <-chan Event {
324 out := make(chan Event, 32)
325 go c.run(parent, messages, tools, out)
326 return out
327}
328
329func (c *Client) run(parent context.Context, msgs []chmctx.Message, tools []Tool, out chan<- Event) {
330 defer close(out)
331 start := time.Now()
332
333 // Pre-stream retry: transient failures (proxy hiccups: 5xx, 429, LiteLLM's
334 // transient 404) are resent after a rising backoff instead of killing the
335 // turn. Only here, before any token has streamed — a mid-stream resend
336 // would duplicate content already in the transcript. Probe stays
337 // retry-free: its job is fast feedback on a misconfigured profile.
338 resp, errEvt := c.sendChat(parent, msgs, tools)
339 for attempt := 0; errEvt != nil && attempt < len(c.RetryBackoff) && retryable(errEvt.Err); attempt++ {
340 delay := c.RetryBackoff[attempt]
341 hint := fmt.Sprintf("retry %d/%d in %s", attempt+1, len(c.RetryBackoff), delay)

Callers 1

ChatMethod · 0.95

Calls 6

sendChatMethod · 0.95
FromHeadersFunction · 0.92
ContextWindowFromHeadersFunction · 0.92
sendEventFunction · 0.85
readSSEFunction · 0.85
ResetMethod · 0.80

Tested by

no test coverage detected