This commit is contained in:
despiegk 2025-05-23 09:33:05 +04:00
parent a16ac8f627
commit 79d66e4b6b
34 changed files with 603 additions and 608 deletions

View File

@ -25,9 +25,6 @@ import (
"fmt"
"log"
"os"
"github.com/freeflowuniverse/herolauncher/pkg/herolauncher"
_ "github.com/freeflowuniverse/herolauncher/pkg/herolauncher/docs" // Import generated swagger docs
)
func main() {

2
go.mod
View File

@ -71,7 +71,7 @@ require (
github.com/metoro-io/mcp-golang v0.8.0 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/openai/openai-go v0.1.0-beta.9 // indirect
github.com/openaiproxy/openaiproxy-go v0.1.0-beta.9 // indirect
github.com/pb33f/libopenapi v0.21.8 // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect

4
go.sum
View File

@ -160,8 +160,8 @@ github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssn
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/openai/openai-go v0.1.0-beta.9 h1:ABpubc5yU/3ejee2GgRrbFta81SG/d7bQbB8mIdP0Xo=
github.com/openai/openai-go v0.1.0-beta.9/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
github.com/openaiproxy/openaiproxy-go v0.1.0-beta.9 h1:ABpubc5yU/3ejee2GgRrbFta81SG/d7bQbB8mIdP0Xo=
github.com/openaiproxy/openaiproxy-go v0.1.0-beta.9/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
github.com/pb33f/libopenapi v0.21.8 h1:Fi2dAogMwC6av/5n3YIo7aMOGBZH/fBMO4OnzFB3dQA=
github.com/pb33f/libopenapi v0.21.8/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU=
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=

View File

@ -34,7 +34,7 @@ Jobs are stored in both Redis and OurDB:
- Handles all queue operations (adding/removing jobs)
- Stores all running jobs for fast access
- Used for real-time operations and status updates
- **Job Storage**: `herojobs:<circleID>:<topic>:<jobID>` or legacy `jobsmanager:<jobID>`
- **Job Storage**: `herojobs:<circleID>:<topic>:<jobID>` or legacy `herojobs:<jobID>`
- **Queue**: `heroqueue:<circleID>:<topic>`
#### OurDB
@ -85,14 +85,14 @@ The watchdog uses Go's concurrency primitives to safely manage multiple jobs:
```go
// Initialize Redis client
redisClient, err := jobsmanager.NewRedisClient("localhost:6379", false)
redisClient, err := herojobs.NewRedisClient("localhost:6379", false)
if err != nil {
log.Fatalf("Failed to connect to Redis: %v", err)
}
defer redisClient.Close()
// Create and start watchdog
watchdog := jobsmanager.NewWatchDog(redisClient)
watchdog := herojobs.NewWatchDog(redisClient)
watchdog.Start()
// Handle shutdown
@ -103,14 +103,14 @@ defer watchdog.Stop()
```go
// Create a new job
job := jobsmanager.NewJob()
job := herojobs.NewJob()
job.CircleID = "myCircle"
job.Topic = "myTopic"
job.Params = `
!!fake.return_success
message: "This is a test job"
`
job.ParamsType = jobsmanager.ParamsTypeHeroScript
job.ParamsType = herojobs.ParamsTypeHeroScript
job.Timeout = 30 // 30 seconds timeout
// Save the job to OurDB to get an ID
@ -140,7 +140,7 @@ jobID := job.JobID
job, err := redisClient.GetJob(jobID)
if err != nil {
// If not found in Redis, try OurDB for historical jobs
job = &jobsmanager.Job{JobID: jobID}
job = &herojobs.Job{JobID: jobID}
if err := job.Load(); err != nil {
log.Printf("Failed to load job: %v", err)
return
@ -149,13 +149,13 @@ if err != nil {
// Check job status
switch job.Status {
case jobsmanager.JobStatusNew:
case herojobs.JobStatusNew:
fmt.Println("Job is waiting to be processed")
case jobsmanager.JobStatusActive:
case herojobs.JobStatusActive:
fmt.Println("Job is currently being processed")
case jobsmanager.JobStatusDone:
case herojobs.JobStatusDone:
fmt.Printf("Job completed successfully: %s\n", job.Result)
case jobsmanager.JobStatusError:
case herojobs.JobStatusError:
fmt.Printf("Job failed: %s\n", job.Error)
}
```

View File

@ -1,4 +1,4 @@
package jobsmanager
package herojobs
import (
"encoding/json"

View File

@ -1,4 +1,4 @@
package jobsmanager
package herojobs
import (
"context"

View File

@ -1,4 +1,4 @@
package jobsmanager
package herojobs
import (
"context"
@ -82,7 +82,7 @@ func (r *RedisClient) GetJob(jobID interface{}) (*Job, error) {
switch id := jobID.(type) {
case uint32:
// Legacy format for backward compatibility
storageKey = fmt.Sprintf("jobsmanager:%d", id)
storageKey = fmt.Sprintf("herojobs:%d", id)
case string:
// Check if this is a composite key (circleID:topic:jobID)
parts := strings.Split(id, ":")
@ -103,10 +103,10 @@ func (r *RedisClient) GetJob(jobID interface{}) (*Job, error) {
// Try to convert string to uint32 (legacy format)
var numericID uint32
if _, err := fmt.Sscanf(id, "%d", &numericID); err == nil {
storageKey = fmt.Sprintf("jobsmanager:%d", numericID)
storageKey = fmt.Sprintf("herojobs:%d", numericID)
} else {
// Legacy string ID format
storageKey = fmt.Sprintf("jobsmanager:%s", id)
storageKey = fmt.Sprintf("herojobs:%s", id)
}
}
default:
@ -139,7 +139,7 @@ func (r *RedisClient) DeleteJob(jobID interface{}) error {
switch id := jobID.(type) {
case uint32:
// Legacy format for backward compatibility
storageKey = fmt.Sprintf("jobsmanager:%d", id)
storageKey = fmt.Sprintf("herojobs:%d", id)
case string:
// Check if this is a composite key (circleID:topic:jobID)
parts := strings.Split(id, ":")
@ -160,10 +160,10 @@ func (r *RedisClient) DeleteJob(jobID interface{}) error {
// Try to convert string to uint32 (legacy format)
var numericID uint32
if _, err := fmt.Sscanf(id, "%d", &numericID); err == nil {
storageKey = fmt.Sprintf("jobsmanager:%d", numericID)
storageKey = fmt.Sprintf("herojobs:%d", numericID)
} else {
// Legacy string ID format
storageKey = fmt.Sprintf("jobsmanager:%s", id)
storageKey = fmt.Sprintf("herojobs:%s", id)
}
}
default:
@ -238,7 +238,7 @@ func (r *RedisClient) QueueEmpty(circleID, topic string) error {
}
} else {
// Handle legacy string IDs
storageKey := fmt.Sprintf("jobsmanager:%s", jobIDStr)
storageKey := fmt.Sprintf("herojobs:%s", jobIDStr)
err := r.client.Del(r.ctx, storageKey).Err()
if err != nil {
return fmt.Errorf("failed to delete job %s: %w", jobIDStr, err)

View File

@ -1,4 +1,4 @@
package jobsmanager
package herojobs
import (
"context"

View File

@ -4,7 +4,7 @@ import (
"log"
"sync"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/herohandler"
)
func main() {

View File

@ -2,8 +2,6 @@ package main
import (
"fmt"
"log"
"os"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
)
@ -15,26 +13,26 @@ func main() {
pb := playbook.New()
// Start a simple process
startAction := pb.NewAction(1, "start", "process", 0, playbook.ActionTypeUnknown)
startAction := pb.NewAction("1", "start", "process", 0, playbook.ActionTypeUnknown)
startAction.Params.Set("name", "example_process")
startAction.Params.Set("command", "ping -c 60 localhost")
startAction.Params.Set("log", "true")
// List all processes
listAction := pb.NewAction(2, "list", "process", 0, playbook.ActionTypeUnknown)
listAction := pb.NewAction("2", "list", "process", 0, playbook.ActionTypeUnknown)
listAction.Params.Set("format", "table")
// Get status of a specific process
statusAction := pb.NewAction(3, "status", "process", 0, playbook.ActionTypeUnknown)
statusAction := pb.NewAction("3", "status", "process", 0, playbook.ActionTypeUnknown)
statusAction.Params.Set("name", "example_process")
// Get logs of a specific process
logsAction := pb.NewAction(4, "logs", "process", 0, playbook.ActionTypeUnknown)
logsAction := pb.NewAction("4", "logs", "process", 0, playbook.ActionTypeUnknown)
logsAction.Params.Set("name", "example_process")
logsAction.Params.Set("lines", "10")
// Stop a process
stopAction := pb.NewAction(5, "stop", "process", 0, playbook.ActionTypeUnknown)
stopAction := pb.NewAction("5", "stop", "process", 0, playbook.ActionTypeUnknown)
stopAction.Params.Set("name", "example_process")
// Generate the heroscript

View File

@ -9,7 +9,7 @@ import (
"syscall"
"time"
proxy "github.com/freeflowuniverse/heroagent/pkg/proxies/openai"
openaiproxy "github.com/freeflowuniverse/heroagent/pkg/heroservices/openaiproxy"
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
)
@ -37,15 +37,15 @@ func testProxyWithClient() {
// Create a client that points to our proxy
// Note: The server is using "/ai" as the prefix for all routes
client := openai.NewClient(
client := openaiproxy.NewClient(
option.WithAPIKey("test-key"), // This is our test key, not a real OpenAI key
option.WithBaseURL("http://localhost:8080/ai"), // Use the /ai prefix to match the server routes
)
// Create a completion request
chatCompletion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Say this is a test"),
chatCompletion, err := client.Chat.Completions.New(context.Background(), openaiproxy.ChatCompletionNewParams{
Messages: []openaiproxy.ChatCompletionMessageParamUnion{
openaiproxy.UserMessage("Say this is a test"),
},
Model: "gpt-3.5-turbo", // Use a model that our proxy supports
})
@ -70,9 +70,9 @@ func runServerMode() {
// Create a proxy configuration
config := proxy.ProxyConfig{
Port: 8080, // Use a non-privileged port for testing
OpenAIBaseURL: "https://api.openai.com", // Default OpenAI API URL
DefaultOpenAIKey: openaiKey, // Fallback API key if user doesn't have one
Port: 8080, // Use a non-privileged port for testing
OpenAIBaseURL: "https://api.openaiproxy.com", // Default OpenAI API URL
DefaultOpenAIKey: openaiKey, // Fallback API key if user doesn't have one
}
// Create a new factory with the configuration

View File

@ -119,7 +119,7 @@ func createJob(c *fiber.Ctx, apiKey string, endpoint string, requestBody interfa
// Create a new job
job := jobsmanager.NewJob()
job.ParamsType = jobsmanager.ParamsTypeAI
job.Topic = "openai-proxy"
job.Topic = "openaiproxy-proxy"
job.CircleID = "ai"
// Serialize request body to JSON
@ -183,7 +183,7 @@ func (s *Server) handleModels(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/models"
if url == "/v1/models" {
url = "https://api.openai.com/v1/models"
url = "https://api.openaiproxy.com/v1/models"
}
req, err := http.NewRequest("GET", url, nil)
@ -250,7 +250,7 @@ func (s *Server) handleGetModel(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/models/" + modelID
if strings.HasPrefix(url, "/v1/models/") {
url = "https://api.openai.com/v1/models/" + modelID
url = "https://api.openaiproxy.com/v1/models/" + modelID
}
req, err := http.NewRequest("GET", url, nil)
@ -337,7 +337,7 @@ func (s *Server) handleChatCompletions(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/chat/completions"
if url == "/v1/chat/completions" {
url = "https://api.openai.com/v1/chat/completions"
url = "https://api.openaiproxy.com/v1/chat/completions"
}
// Convert the request body back to JSON
@ -461,7 +461,7 @@ func (s *Server) handleCompletions(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/completions"
if url == "/v1/completions" {
url = "https://api.openai.com/v1/completions"
url = "https://api.openaiproxy.com/v1/completions"
}
// Convert the request body back to JSON
@ -585,7 +585,7 @@ func (s *Server) handleEmbeddings(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/embeddings"
if url == "/v1/embeddings" {
url = "https://api.openai.com/v1/embeddings"
url = "https://api.openaiproxy.com/v1/embeddings"
}
// Convert the request body back to JSON
@ -724,7 +724,7 @@ func (s *Server) handleImagesGenerations(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/images/generations"
if url == "/v1/images/generations" {
url = "https://api.openai.com/v1/images/generations"
url = "https://api.openaiproxy.com/v1/images/generations"
}
// Convert the request body back to JSON
@ -919,7 +919,7 @@ func (s *Server) handleListFiles(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/files"
if url == "/v1/files" {
url = "https://api.openai.com/v1/files"
url = "https://api.openaiproxy.com/v1/files"
}
req, err := http.NewRequest("GET", url, nil)
@ -1017,7 +1017,7 @@ func (s *Server) handleGetFile(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/files/" + fileID
if strings.HasPrefix(url, "/v1/files/") {
url = "https://api.openai.com/v1/files/" + fileID
url = "https://api.openaiproxy.com/v1/files/" + fileID
}
req, err := http.NewRequest("GET", url, nil)
@ -1084,7 +1084,7 @@ func (s *Server) handleDeleteFile(c *fiber.Ctx) error {
// Forward request to OpenAI
url := s.Factory.Config.OpenAIBaseURL + "/v1/files/" + fileID
if strings.HasPrefix(url, "/v1/files/") {
url = "https://api.openai.com/v1/files/" + fileID
url = "https://api.openaiproxy.com/v1/files/" + fileID
}
req, err := http.NewRequest("DELETE", url, nil)

View File

@ -6,7 +6,7 @@ import (
"fmt"
"net"
"github.com/freeflowuniverse/herolauncher/pkg/openrpcmanager"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
)
// Common errors

View File

@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/freeflowuniverse/herolauncher/pkg/openrpcmanager"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
)
// MockClient implements the Client interface for testing

View File

@ -9,7 +9,7 @@ import (
"os/signal"
"syscall"
"github.com/freeflowuniverse/herolauncher/pkg/openrpcmanager"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
)
func main() {

View File

@ -91,20 +91,20 @@ func (b *Builder) Build() error {
return fmt.Errorf("failed to ensure Go is installed: %w", err)
}
fmt.Printf("Using Go executable from: %s\n", goPath)
// Pass the Go path explicitly to the GoSPBuilder
b.GoSPBuilder.WithGoPath(goPath)
// For the Go stored procedure, we'll create and execute a shell script directly
// to ensure all environment variables are properly set
fmt.Println("Building Go stored procedure via shell script...")
tempDir, err := os.MkdirTemp("", "gosp-build-")
if err != nil {
return fmt.Errorf("failed to create temp directory: %w", err)
}
defer os.RemoveAll(tempDir)
// Create the Go source file in the temp directory
libPath := filepath.Join(tempDir, "gosp.go")
libSrc := `
@ -122,7 +122,7 @@ func main() {}
if err := os.WriteFile(libPath, []byte(libSrc), 0644); err != nil {
return fmt.Errorf("failed to write Go source file: %w", err)
}
// Create a shell script to build the Go stored procedure
buildScript := filepath.Join(tempDir, "build.sh")
buildScriptContent := fmt.Sprintf(`#!/bin/sh
@ -147,11 +147,11 @@ go build -buildmode=c-shared -o %s/lib/libgosp.so %s
echo "Go stored procedure built successfully!"
`,
libPath, b.InstallPrefix, b.InstallPrefix, b.InstallPrefix, libPath, b.InstallPrefix, libPath)
if err := os.WriteFile(buildScript, []byte(buildScriptContent), 0755); err != nil {
return fmt.Errorf("failed to write build script: %w", err)
}
// Execute the build script
cmd := exec.Command("/bin/sh", buildScript)
cmd.Stdout = os.Stdout