Skip to content

Commit 3fd0ea1

Browse files
committed
feat: implement AI API service binding (#9)
1 parent 3c48a24 commit 3fd0ea1

File tree

12 files changed

+587
-72
lines changed

12 files changed

+587
-72
lines changed

binding/ai/service.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package ai
2+
3+
import (
4+
"context"
5+
"fmt"
6+
)
7+
8+
// Service provides AI methods for the frontend
9+
type Service struct {
10+
ctx context.Context
11+
}
12+
13+
// NewService creates a new AI Service
14+
func NewService() *Service {
15+
return &Service{}
16+
}
17+
18+
// SetContext sets the context for the service
19+
func (s *Service) SetContext(ctx context.Context) {
20+
s.ctx = ctx
21+
}
22+
23+
// TextRequest defines the parameters for text generation
24+
type TextRequest struct {
25+
Prompt string `json:"prompt"`
26+
Model string `json:"model"`
27+
Temperature *float64 `json:"temperature,omitempty"`
28+
MaxTokens *int `json:"maxTokens,omitempty"`
29+
Options map[string]interface{} `json:"options,omitempty"`
30+
}
31+
32+
// ImageRequest defines the parameters for image generation
33+
type ImageRequest struct {
34+
Prompt string `json:"prompt"`
35+
Model string `json:"model"`
36+
Size string `json:"size,omitempty"`
37+
Quality string `json:"quality,omitempty"`
38+
Style string `json:"style,omitempty"`
39+
Options map[string]interface{} `json:"options,omitempty"`
40+
}
41+
42+
// VideoRequest defines the parameters for video generation
43+
type VideoRequest struct {
44+
Prompt string `json:"prompt"`
45+
Model string `json:"model"`
46+
Duration string `json:"duration,omitempty"`
47+
Resolution string `json:"resolution,omitempty"`
48+
Options map[string]interface{} `json:"options,omitempty"`
49+
}
50+
51+
// AudioRequest defines the parameters for audio generation
52+
type AudioRequest struct {
53+
Prompt string `json:"prompt"`
54+
Model string `json:"model"`
55+
Voice string `json:"voice,omitempty"`
56+
Speed *float64 `json:"speed,omitempty"`
57+
Options map[string]interface{} `json:"options,omitempty"`
58+
}
59+
60+
// AIResponse defines the common response structure for AI requests
61+
type AIResponse struct {
62+
Content string `json:"content"`
63+
Usage map[string]interface{} `json:"usage,omitempty"`
64+
Raw interface{} `json:"raw,omitempty"`
65+
}
66+
67+
// GenerateText generates text based on the prompt
68+
func (s *Service) GenerateText(req TextRequest) (*AIResponse, error) {
69+
// TODO: Implement actual AI call
70+
return &AIResponse{
71+
Content: fmt.Sprintf("Generated text for prompt: %s using model: %s", req.Prompt, req.Model),
72+
}, nil
73+
}
74+
75+
// GenerateImage generates an image based on the prompt
76+
func (s *Service) GenerateImage(req ImageRequest) (*AIResponse, error) {
77+
// TODO: Implement actual AI call
78+
return &AIResponse{
79+
Content: fmt.Sprintf("Generated image URL/data for prompt: %s using model: %s", req.Prompt, req.Model),
80+
}, nil
81+
}
82+
83+
// GenerateVideo generates a video based on the prompt
84+
func (s *Service) GenerateVideo(req VideoRequest) (*AIResponse, error) {
85+
// TODO: Implement actual AI call
86+
return &AIResponse{
87+
Content: fmt.Sprintf("Generated video URL/data for prompt: %s using model: %s", req.Prompt, req.Model),
88+
}, nil
89+
}
90+
91+
// GenerateAudio generates audio based on the prompt
92+
func (s *Service) GenerateAudio(req AudioRequest) (*AIResponse, error) {
93+
// TODO: Implement actual AI call
94+
return &AIResponse{
95+
Content: fmt.Sprintf("Generated audio URL/data for prompt: %s using model: %s", req.Prompt, req.Model),
96+
}, nil
97+
}

binding/database/service.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ func NewService() *Service {
1414

1515
// GetAIConfig returns the configuration for a specific provider
1616
func (s *Service) GetAIConfig(provider string) (*db.AIConfig, error) {
17-
return db.GetConfig(db.AIProvider(provider))
17+
return db.GetAIConfig(db.AIProvider(provider))
1818
}
1919

2020
// SaveAIConfig saves the configuration for a specific provider
2121
func (s *Service) SaveAIConfig(config db.AIConfig) error {
22-
return db.SaveConfig(&config)
22+
return db.SaveAIConfig(&config)
2323
}
2424

2525
// DeleteAIConfig deletes the configuration for a specific provider
2626
func (s *Service) DeleteAIConfig(provider string) error {
27-
return db.DeleteConfig(db.AIProvider(provider))
27+
return db.DeleteAIConfig(db.AIProvider(provider))
2828
}
2929

3030
// ListAIConfigs returns all AI configurations
3131
func (s *Service) ListAIConfigs() ([]db.AIConfig, error) {
32-
return db.ListConfigs()
32+
return db.ListAIConfigs()
3333
}

database/repository.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"log"
77
)
88

9-
func GetConfig(provider AIProvider) (*AIConfig, error) {
9+
func GetAIConfig(provider AIProvider) (*AIConfig, error) {
1010
config := AIConfig{}
1111
err := DB.Get(&config, "SELECT * FROM ai_configs WHERE provider = ?", provider)
1212
if err != nil {
@@ -18,7 +18,7 @@ func GetConfig(provider AIProvider) (*AIConfig, error) {
1818
return &config, nil
1919
}
2020

21-
func SaveConfig(config *AIConfig) error {
21+
func SaveAIConfig(config *AIConfig) error {
2222
log.Println("Saving config:", config)
2323
if config.Provider == "" {
2424
return fmt.Errorf("provider is required")
@@ -37,12 +37,12 @@ func SaveConfig(config *AIConfig) error {
3737
return err
3838
}
3939

40-
func DeleteConfig(provider AIProvider) error {
40+
func DeleteAIConfig(provider AIProvider) error {
4141
_, err := DB.Exec("DELETE FROM ai_configs WHERE provider = ?", provider)
4242
return err
4343
}
4444

45-
func ListConfigs() ([]AIConfig, error) {
45+
func ListAIConfigs() ([]AIConfig, error) {
4646
configs := []AIConfig{}
4747
err := DB.Select(&configs, "SELECT * FROM ai_configs ORDER BY created_at DESC")
4848
return configs, err
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Sparkles } from "lucide-react";
2+
3+
export function AboutSettings() {
4+
return (
5+
<div className="p-8 max-w-2xl h-full flex flex-col">
6+
<div className="flex-1 space-y-8">
7+
<div className="flex flex-col items-center justify-center py-10 space-y-4">
8+
<div className="w-20 h-20 bg-primary/10 rounded-2xl flex items-center justify-center">
9+
<Sparkles className="w-10 h-10 text-primary" />
10+
</div>
11+
<div className="text-center">
12+
<h3 className="text-2xl font-bold">Firebringer</h3>
13+
<p className="text-sm text-muted-foreground">Version 0.0.1</p>
14+
</div>
15+
<p className="text-sm leading-relaxed text-center">
16+
Firebringer 是一个可视化工作流编排工具,通过直观的节点式界面,
17+
让你轻松设计和管理 AI 驱动的智能工作流。
18+
</p>
19+
</div>
20+
</div>
21+
22+
<div className="text-center text-xs text-muted-foreground pt-8">
23+
© 2026 Firebringer. All rights reserved.
24+
</div>
25+
</div>
26+
);
27+
}

frontend/src/components/settings/ai-model-settings.tsx

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -55,60 +55,66 @@ export function AIModelSettings() {
5555
};
5656

5757
return (
58-
<div className="space-y-8">
59-
{PROVIDERS.map((provider, index) => {
60-
const config = configs[provider] || new database.AIConfig({ provider });
58+
<div className="p-8 max-w-3xl">
59+
<div className="mb-6">
60+
<h3 className="text-lg font-medium">模型服务</h3>
61+
<p className="text-sm text-muted-foreground">配置各大 AI 模型的访问凭证。</p>
62+
</div>
63+
<div className="space-y-8">
64+
{PROVIDERS.map((provider, index) => {
65+
const config = configs[provider] || new database.AIConfig({ provider });
6166

62-
return (
63-
<div key={provider} className="space-y-4">
64-
{index > 0 && <Separator className="my-6" />}
67+
return (
68+
<div key={provider} className="space-y-4">
69+
{index > 0 && <Separator className="my-6" />}
6570

66-
<div className="flex items-center justify-between">
67-
<div>
68-
<h4 className="text-base font-medium capitalize">{provider}</h4>
69-
<p className="text-sm text-muted-foreground">
70-
Configure connection details for {provider}.
71-
</p>
71+
<div className="flex items-center justify-between">
72+
<div>
73+
<h4 className="text-base font-medium capitalize">{provider}</h4>
74+
<p className="text-sm text-muted-foreground">
75+
Configure connection details for {provider}.
76+
</p>
77+
</div>
78+
<Button
79+
variant="outline"
80+
size="sm"
81+
onClick={() => handleSave(provider)}
82+
disabled={loading}
83+
>
84+
保存
85+
</Button>
7286
</div>
73-
<Button
74-
variant="outline"
75-
size="sm"
76-
onClick={() => handleSave(provider)}
77-
disabled={loading}
78-
>
79-
保存
80-
</Button>
81-
</div>
8287

83-
<div className="grid gap-4 bg-muted/30 p-4 rounded-lg border">
84-
<div className="grid gap-2">
85-
<Label htmlFor={`${provider}-key`}>API Key</Label>
86-
<Input
87-
id={`${provider}-key`}
88-
type="password"
89-
className="bg-background"
90-
value={config.apiKey || ""}
91-
onChange={(e) => handleChange(provider, "apiKey", e.target.value)}
92-
placeholder={`sk-...`}
93-
/>
94-
</div>
95-
<div className="grid gap-2">
96-
<Label htmlFor={`${provider}-base`}>Base URL</Label>
97-
<Input
98-
id={`${provider}-base`}
99-
className="bg-background"
100-
value={config.baseUrl || ""}
101-
onChange={(e) => handleChange(provider, "baseUrl", e.target.value)}
102-
placeholder="Default"
103-
/>
104-
<p className="text-[0.8rem] text-muted-foreground">
105-
Optional. Leave empty to use the default endpoint.
106-
</p>
88+
<div className="grid gap-4 bg-muted/30 p-4 rounded-lg border">
89+
<div className="grid gap-2">
90+
<Label htmlFor={`${provider}-key`}>API Key</Label>
91+
<Input
92+
id={`${provider}-key`}
93+
type="password"
94+
className="bg-background"
95+
value={config.apiKey || ""}
96+
onChange={(e) => handleChange(provider, "apiKey", e.target.value)}
97+
placeholder={`sk-...`}
98+
/>
99+
</div>
100+
<div className="grid gap-2">
101+
<Label htmlFor={`${provider}-base`}>Base URL</Label>
102+
<Input
103+
id={`${provider}-base`}
104+
className="bg-background"
105+
value={config.baseUrl || ""}
106+
onChange={(e) => handleChange(provider, "baseUrl", e.target.value)}
107+
placeholder="Default"
108+
/>
109+
<p className="text-[0.8rem] text-muted-foreground">
110+
Optional. Leave empty to use the default endpoint.
111+
</p>
112+
</div>
107113
</div>
108114
</div>
109-
</div>
110-
);
111-
})}
115+
);
116+
})}
117+
</div>
112118
</div>
113119
);
114120
}

0 commit comments

Comments
 (0)