Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ Runix esta implementado completamente en Go. Para compilar las herramientas ejec
```bash
go build ./...
```
### Uso de la IA en consola

Runix puede interactuar con [OpenRouter](https://openrouter.ai) para generar respuestas mediante IA. Define la variable de entorno `OPENROUTER_API_KEY` y utiliza el comando `chat`:

```bash
go run ./go/cli chat --context "Hola" "¿Cómo estás?"
```


---

Expand Down
69 changes: 67 additions & 2 deletions go/cli/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,72 @@
package main

import "fmt"
import (
"flag"
"fmt"
"os"

"runix/go/openrouter"
)

func main() {
fmt.Println("¡Bienvenido a Runix en Go!")
if len(os.Args) < 2 {
usage()
return
}

switch os.Args[1] {
case "chat":
chatCmd(os.Args[2:])
case "create":
createCmd(os.Args[2:])
default:
fmt.Println("Unknown command:", os.Args[1])
usage()
}
}

func usage() {
fmt.Println("Runix CLI")
fmt.Println("Usage:")
fmt.Println(" runix chat [options] <message>")
fmt.Println(" runix create <directory>")
}

func chatCmd(args []string) {
fs := flag.NewFlagSet("chat", flag.ExitOnError)
context := fs.String("context", "", "context prompt")
model := fs.String("model", "mistralai/mistral-small-3.2-24b-instruct:free", "model")
fs.Parse(args)

if fs.NArg() < 1 {
fmt.Println("message is required")
return
}

apiKey := os.Getenv("OPENROUTER_API_KEY")
if apiKey == "" {
fmt.Println("OPENROUTER_API_KEY environment variable not set")
return
}

client := openrouter.NewClient(apiKey)
reply, err := client.Chat(*model, *context, fs.Arg(0))
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(reply)
}

func createCmd(args []string) {
if len(args) < 1 {
fmt.Println("directory path is required")
return
}
path := args[0]
if err := os.MkdirAll(path, 0755); err != nil {
fmt.Println("error creating directory:", err)
return
}
fmt.Println("created directory", path)
}
84 changes: 84 additions & 0 deletions go/openrouter/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package openrouter

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)

type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}

type chatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
}

type chatResponse struct {
Choices []struct {
Message Message `json:"message"`
} `json:"choices"`
}

type Client struct {
apiKey string
baseURL string
httpClient *http.Client
}

// NewClient returns an OpenRouter API client.
func NewClient(apiKey string) *Client {
return &Client{
apiKey: apiKey,
baseURL: "https://openrouter.ai/api/v1",
httpClient: &http.Client{},
}
}

// Chat sends a message with an optional context prompt to the given model.
func (c *Client) Chat(model, context, message string) (string, error) {
reqBody := chatRequest{
Model: model,
Messages: []Message{
{Role: "system", Content: context},
{Role: "user", Content: message},
},
}
data, err := json.Marshal(reqBody)
if err != nil {
return "", err
}

req, err := http.NewRequest("POST", c.baseURL+"/chat/completions", bytes.NewReader(data))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.apiKey)
req.Header.Set("HTTP-Referer", "https://github.com/cordlang/runix")
req.Header.Set("User-Agent", "runix-cli")

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("openrouter error: %s", string(body))
}

var r chatResponse
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
return "", err
}
if len(r.Choices) == 0 {
return "", fmt.Errorf("no choices returned")
}
return r.Choices[0].Message.Content, nil
}
Loading