Skip to content

Commit 662c92e

Browse files
author
Jawher Kallel
committed
add and implement anthropic and gemini api services
1 parent a900e91 commit 662c92e

File tree

5 files changed

+170
-5
lines changed

5 files changed

+170
-5
lines changed

gateway/internal/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ type Config struct {
1414
GroqKey string
1515
RedisAddr string
1616
OpenRouterKey string
17+
AnthropicKey string
18+
GeminiKey string
1719
}
1820

1921
func Load() *Config {
@@ -28,6 +30,8 @@ func Load() *Config {
2830
HuggingFaceKey: getEnv("HF_API_KEY", ""),
2931
GroqKey: getEnv("GROQ_API_KEY", ""),
3032
OpenRouterKey: getEnv("OPENROUTER_API_KEY", ""),
33+
AnthropicKey: getEnv("ANTHROPIC_API_KEY", ""),
34+
GeminiKey: getEnv("GEMINI_API_KEY", ""),
3135
RedisAddr: getEnv("REDIS_ADDR", "localhost:6379"),
3236
}
3337
if cfg.OpenAIKey == "" {

gateway/internal/handlers/query.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ func QueryHandler(cfg *config.Config, cache *cache.Cache) gin.HandlerFunc {
4848
response, err = services.QueryGroq(cfg.GroqKey, req.Prompt)
4949
case "openrouter":
5050
response, err = services.QueryOpenRouter(cfg.OpenRouterKey, req.Prompt)
51+
case "anthropic":
52+
response, err = services.QueryAnthropic(cfg.AnthropicKey, req.Prompt)
53+
case "gemini":
54+
response, err = services.QueryGemini(cfg.GeminiKey, req.Prompt)
5155
default:
5256
c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported provider"})
5357
return
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package services
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
)
10+
11+
type AnthropicMessage struct {
12+
Role string `json:"role"`
13+
Content string `json:"content"`
14+
}
15+
16+
type AnthropicRequest struct {
17+
Model string `json:"model"`
18+
MaxTokens int `json:"max_tokens"`
19+
Messages []AnthropicMessage `json:"messages"`
20+
}
21+
22+
type AnthropicResponse struct {
23+
Content string `json:"content"`
24+
Error struct {
25+
Message string `json:"message"`
26+
} `json:"error"`
27+
}
28+
29+
func QueryAnthropic(apiKey, prompt string) (string, error) {
30+
reqBody := AnthropicRequest{
31+
Model: "claude-sonnet-4-20250514",
32+
MaxTokens: 1000,
33+
Messages: []AnthropicMessage{
34+
{Role: "user", Content: prompt},
35+
},
36+
}
37+
jsonData, _ := json.Marshal(reqBody)
38+
39+
req, _ := http.NewRequest("POST", "https://api.anthropic.com/v1/messages", bytes.NewBuffer(jsonData))
40+
req.Header.Set("x-api-key", apiKey)
41+
req.Header.Set("Content-Type", "application/json")
42+
req.Header.Set("anthropic-version", "2023-06-01")
43+
44+
client := &http.Client{}
45+
resp, err := client.Do(req)
46+
if err != nil {
47+
return "", err
48+
}
49+
defer resp.Body.Close()
50+
51+
bodyBytes, _ := io.ReadAll(resp.Body)
52+
if resp.StatusCode != http.StatusOK {
53+
var errResp AnthropicResponse
54+
json.Unmarshal(bodyBytes, &errResp)
55+
if errResp.Error.Message != "" {
56+
return "", fmt.Errorf("Anthropic error: %s", errResp.Error.Message)
57+
}
58+
return "", fmt.Errorf("Anthropic API error: %s", string(bodyBytes))
59+
}
60+
61+
// Adjust Anthropic's response as needed
62+
var result map[string]interface{}
63+
if err := json.Unmarshal(bodyBytes, &result); err != nil {
64+
return "", fmt.Errorf("failed to parse Anthropic response: %w", err)
65+
}
66+
// Extract the content from the first message
67+
if messages, ok := result["content"].(string); ok && messages != "" {
68+
return messages, nil
69+
}
70+
if messages, ok := result["content"].([]interface{}); ok && len(messages) > 0 {
71+
if msg, ok := messages[0].(map[string]interface{}); ok {
72+
if content, ok := msg["text"].(string); ok {
73+
return content, nil
74+
}
75+
}
76+
}
77+
return "", fmt.Errorf("no content returned from Anthropic: %s", string(bodyBytes))
78+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package services
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
)
10+
11+
type GeminiPart struct {
12+
Text string `json:"text"`
13+
}
14+
15+
type GeminiContent struct {
16+
Parts []GeminiPart `json:"parts"`
17+
}
18+
19+
type GeminiRequest struct {
20+
Contents []GeminiContent `json:"contents"`
21+
}
22+
23+
type GeminiResponse struct {
24+
Candidates []struct {
25+
Content struct {
26+
Parts []struct {
27+
Text string `json:"text"`
28+
} `json:"parts"`
29+
} `json:"content"`
30+
} `json:"candidates"`
31+
Error struct {
32+
Message string `json:"message"`
33+
} `json:"error"`
34+
}
35+
36+
func QueryGemini(apiKey, prompt string) (string, error) {
37+
reqBody := GeminiRequest{
38+
Contents: []GeminiContent{
39+
{
40+
Parts: []GeminiPart{
41+
{Text: prompt},
42+
},
43+
},
44+
},
45+
}
46+
jsonData, _ := json.Marshal(reqBody)
47+
48+
url := "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
49+
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
50+
req.Header.Set("Content-Type", "application/json")
51+
req.Header.Set("X-goog-api-key", apiKey)
52+
53+
client := &http.Client{}
54+
resp, err := client.Do(req)
55+
if err != nil {
56+
return "", err
57+
}
58+
defer resp.Body.Close()
59+
60+
bodyBytes, _ := io.ReadAll(resp.Body)
61+
if resp.StatusCode != http.StatusOK {
62+
var errResp GeminiResponse
63+
json.Unmarshal(bodyBytes, &errResp)
64+
if errResp.Error.Message != "" {
65+
return "", fmt.Errorf("Gemini error: %s", errResp.Error.Message)
66+
}
67+
return "", fmt.Errorf("Gemini API error: %s", string(bodyBytes))
68+
}
69+
70+
var result GeminiResponse
71+
if err := json.Unmarshal(bodyBytes, &result); err != nil {
72+
return "", fmt.Errorf("failed to parse Gemini response: %w", err)
73+
}
74+
75+
if len(result.Candidates) > 0 && len(result.Candidates[0].Content.Parts) > 0 {
76+
return result.Candidates[0].Content.Parts[0].Text, nil
77+
}
78+
return "", fmt.Errorf("no candidates returned from Gemini: %s", string(bodyBytes))
79+
}

infra/docker-compose.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ services:
2626
volumes:
2727
- ../../ops/grafana:/var/lib/grafana
2828

29-
admin:
30-
build: ../admin
31-
ports: ["3010:3010"]
32-
depends_on:
33-
- redis
29+
#admin:
30+
# build: ../admin
31+
# ports: ["3010:3010"]
32+
# depends_on:
33+
# - redis

0 commit comments

Comments
 (0)