| 195 | } |
| 196 | |
| 197 | func (p *OpenCodeProvider) sendMessageAsync(sessionID string, systemPrompt string, text string) error { |
| 198 | reqBody := map[string]interface{}{ |
| 199 | "parts": []map[string]string{ |
| 200 | {"type": "text", "text": text}, |
| 201 | }, |
| 202 | } |
| 203 | if systemPrompt != "" { |
| 204 | reqBody["system"] = systemPrompt |
| 205 | } |
| 206 | |
| 207 | bodyBytes, err := json.Marshal(reqBody) |
| 208 | if err != nil { |
| 209 | return err |
| 210 | } |
| 211 | |
| 212 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) |
| 213 | defer cancel() |
| 214 | |
| 215 | req, err := http.NewRequestWithContext(ctx, "POST", p.serverUrl+"/session/"+sessionID+"/prompt_async", bytes.NewReader(bodyBytes)) |
| 216 | if err != nil { |
| 217 | return err |
| 218 | } |
| 219 | |
| 220 | p.setAuth(req) |
| 221 | req.Header.Set("Content-Type", "application/json") |
| 222 | |
| 223 | resp, err := p.client.Do(req) |
| 224 | if err != nil { |
| 225 | return fmt.Errorf("OpenCode: failed to send message: %v", err) |
| 226 | } |
| 227 | defer resp.Body.Close() |
| 228 | |
| 229 | if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { |
| 230 | respBody, _ := io.ReadAll(resp.Body) |
| 231 | return fmt.Errorf("OpenCode: HTTP %d: %s", resp.StatusCode, string(respBody)) |
| 232 | } |
| 233 | |
| 234 | io.Copy(io.Discard, resp.Body) |
| 235 | return nil |
| 236 | } |
| 237 | |
| 238 | func (p *OpenCodeProvider) readSSEStream(ctx context.Context, sessionID string, question string, writer io.Writer, flusher http.Flusher, ready chan<- struct{}) (*ModelResult, error) { |
| 239 | // OpenCode only exposes global SSE endpoints (/event and /global/event). |