Implemented automatic addition of "// Last modified: [timestamp]" headers across command and internal files for better tracking. Updated prompts in agent and edit commands to enforce header format. Added logic to prepend header if missing in generated content. Fixed minor issues like missing newlines at end of files.
100 lines
2.4 KiB
Go
100 lines
2.4 KiB
Go
// Last modified: 2026-02-28 22:43:59 GMT
|
|
package grok
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/fatih/color"
|
|
)
|
|
|
|
type Client struct {
|
|
APIKey string
|
|
BaseURL string
|
|
}
|
|
|
|
func NewClient() *Client {
|
|
key := os.Getenv("XAI_API_KEY")
|
|
if key == "" {
|
|
color.Red("Error: XAI_API_KEY environment variable not set")
|
|
os.Exit(1)
|
|
}
|
|
return &Client{
|
|
APIKey: key,
|
|
BaseURL: "https://api.x.ai/v1",
|
|
}
|
|
}
|
|
|
|
// Stream prints live to terminal (used by non-TUI commands)
|
|
func (c *Client) Stream(messages []map[string]string, model string) string {
|
|
return c.streamInternal(messages, model, true)
|
|
}
|
|
|
|
// StreamSilent returns the full text without printing (used by TUI and agent)
|
|
func (c *Client) StreamSilent(messages []map[string]string, model string) string {
|
|
return c.streamInternal(messages, model, false)
|
|
}
|
|
|
|
func (c *Client) streamInternal(messages []map[string]string, model string, printLive bool) string {
|
|
url := c.BaseURL + "/chat/completions"
|
|
payload := map[string]interface{}{
|
|
"model": model,
|
|
"messages": messages,
|
|
"temperature": 0.7,
|
|
"stream": true,
|
|
}
|
|
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
|
|
req.Header.Set("Authorization", "Bearer "+c.APIKey)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
color.Red("Request failed: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var fullReply strings.Builder
|
|
scanner := bufio.NewScanner(resp.Body)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if strings.HasPrefix(line, "data: ") {
|
|
data := strings.TrimPrefix(line, "data: ")
|
|
if data == "[DONE]" {
|
|
break
|
|
}
|
|
var chunk map[string]any
|
|
if json.Unmarshal([]byte(data), &chunk) == nil {
|
|
if choices, ok := chunk["choices"].([]any); ok && len(choices) > 0 {
|
|
if delta, ok := choices[0].(map[string]any)["delta"].(map[string]any); ok {
|
|
if content, ok := delta["content"].(string); ok && content != "" {
|
|
fullReply.WriteString(content)
|
|
if printLive {
|
|
fmt.Print(content)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if printLive {
|
|
fmt.Println()
|
|
}
|
|
return fullReply.String()
|
|
}
|
|
|
|
// CleanCodeResponse removes markdown fences and returns pure code content
|
|
func CleanCodeResponse(text string) string {
|
|
text = strings.ReplaceAll(text, "", "")
|
|
text = strings.ReplaceAll(text, "", "")
|
|
text = strings.TrimSpace(text)
|
|
return text
|
|
} |