llm.go.example 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "strings"
  10. )
  11. // Configuration - Replace with your API credentials
  12. const (
  13. // Option 1: OpenAI API
  14. apiKey = "YOUR_API_KEY_HERE"
  15. apiURL = "https://api.openai.com/v1/chat/completions"
  16. modelName = "gpt-3.5-turbo" // or gpt-4, etc.
  17. // Option 2: Anthropic Claude API (uncomment to use)
  18. // apiKey = "YOUR_API_KEY_HERE"
  19. // apiURL = "https://api.anthropic.com/v1/messages"
  20. // modelName = "claude-3-haiku" // or claude-3-opus, etc.
  21. // Option 3: Local LLM (uncomment to use)
  22. // apiKey = "" // No API key needed for local models
  23. // apiURL = "http://localhost:11434/api/chat" // Ollama example
  24. // modelName = "llama2" // or mixtral, phi, etc.
  25. )
  26. // LLM calls the language model. If stream is nil, returns complete response via return value.
  27. // If stream is provided, streams response chunks to channel and returns empty string.
  28. // Input can be a string (wrapped as user message) or []map[string]string for full message history.
  29. func LLM(input interface{}, stream chan<- string) (string, error) {
  30. // Build messages array
  31. var messages []map[string]string
  32. switch v := input.(type) {
  33. case string:
  34. messages = []map[string]string{
  35. {"role": "user", "content": v},
  36. }
  37. case []map[string]string:
  38. messages = v
  39. default:
  40. return "", fmt.Errorf("invalid input type")
  41. }
  42. // Build request
  43. requestBody := map[string]interface{}{
  44. "model": modelName,
  45. "messages": messages,
  46. "temperature": 0.7,
  47. "max_tokens": 500,
  48. }
  49. if stream != nil {
  50. requestBody["stream"] = true
  51. defer close(stream)
  52. }
  53. jsonBody, err := json.Marshal(requestBody)
  54. if err != nil {
  55. return "", err
  56. }
  57. req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBody))
  58. if err != nil {
  59. return "", err
  60. }
  61. req.Header.Set("Content-Type", "application/json")
  62. if apiKey != "" {
  63. req.Header.Set("Authorization", "Bearer "+apiKey)
  64. }
  65. client := &http.Client{}
  66. resp, err := client.Do(req)
  67. if err != nil {
  68. return "", err
  69. }
  70. defer resp.Body.Close()
  71. if resp.StatusCode != http.StatusOK {
  72. body, _ := io.ReadAll(resp.Body)
  73. return "", fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
  74. }
  75. // Handle streaming response
  76. if stream != nil {
  77. scanner := bufio.NewScanner(resp.Body)
  78. for scanner.Scan() {
  79. line := scanner.Text()
  80. if strings.HasPrefix(line, "data: ") {
  81. data := strings.TrimPrefix(line, "data: ")
  82. if data == "[DONE]" {
  83. return "", nil
  84. }
  85. var chunk map[string]interface{}
  86. if err := json.Unmarshal([]byte(data), &chunk); err == nil {
  87. if choices, ok := chunk["choices"].([]interface{}); ok && len(choices) > 0 {
  88. if choice, ok := choices[0].(map[string]interface{}); ok {
  89. if delta, ok := choice["delta"].(map[string]interface{}); ok {
  90. if content, ok := delta["content"].(string); ok {
  91. stream <- content
  92. }
  93. }
  94. }
  95. }
  96. }
  97. }
  98. }
  99. return "", scanner.Err()
  100. }
  101. // Handle non-streaming response
  102. body, err := io.ReadAll(resp.Body)
  103. if err != nil {
  104. return "", err
  105. }
  106. var response map[string]interface{}
  107. if err := json.Unmarshal(body, &response); err != nil {
  108. return "", err
  109. }
  110. if choices, ok := response["choices"].([]interface{}); ok && len(choices) > 0 {
  111. if choice, ok := choices[0].(map[string]interface{}); ok {
  112. if message, ok := choice["message"].(map[string]interface{}); ok {
  113. if content, ok := message["content"].(string); ok {
  114. return content, nil
  115. }
  116. }
  117. }
  118. }
  119. return "", fmt.Errorf("unexpected response format")
  120. }