diff --git a/terminal-messenger/application/README.md b/terminal-messenger/application/README.md new file mode 100644 index 0000000..9405909 --- /dev/null +++ b/terminal-messenger/application/README.md @@ -0,0 +1,166 @@ +# Terminal Messenger + +A simple, colorful terminal-based messenger application written in Go that connects to a remote messaging endpoint. + +## Features + +- 🎨 **Colorized output** - Blue for your input, green for assistant responses +- ⌨️ **Input history** - Navigate previous messages with arrow keys +- 💬 **Conversation context** - Maintains conversation continuity across messages +- 🚀 **Simple and fast** - Minimal setup, quick startup +- 📝 **Line editing** - Full cursor movement and text editing support + +## Prerequisites + +- Go 1.16 or higher +- Internet connection + +## Installation + +1. **Clone or download the project** + +2. **Initialize Go module** + + ```bash + go mod init messenger + ``` + +3. **Install dependencies** + + ```bash + go get github.com/chzyer/readline + ``` + +4. **Build the application (optional)** + ```bash + go build -o messenger main.go + ``` + +## Usage + +### Running the application + +**Using go run:** + +```bash +go run main.go +``` + +**Or if you built the binary:** + +```bash +./messenger +``` + +### Getting started + +1. When prompted, enter a model name (or press Enter for "default") +2. Start typing your messages +3. The assistant will respond to each message +4. Use arrow keys to navigate through your input history +5. Type `quit` or `exit` to end the session + +### Keyboard shortcuts + +- **↑ (Up arrow)** - Navigate to previous input +- **↓ (Down arrow)** - Navigate to next input +- **←/→ (Left/Right arrows)** - Move cursor within current input +- **Ctrl+A** - Jump to beginning of line +- **Ctrl+E** - Jump to end of line +- **Ctrl+C** - Interrupt/exit application + +## Configuration + +The application connects to: + +``` +https://router.ivastudio.verint.live/ProxyScript/run/67bca862210071627d32ef12/current/basic_messenger +``` + +To change the endpoint, modify the `apiURL` constant in `main.go`. + +### Request format + +The application sends POST requests with the following JSON structure: + +```json +{ + "input": "your message here", + "model": "model name", + "previous_response_id": "previous response ID for context", + "metadata": { + "channel": "text" + } +} +``` + +### Response format + +Expected response structure: + +```json +{ + "id": "response-id", + "object": "response", + "created_at": 1234567890, + "status": "completed", + "model": "Model Name", + "output": [ + { + "type": "message", + "id": "message-id", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Assistant response here", + "annotations": [] + } + ] + } + ] +} +``` + +## Project Structure + +``` +. +├── main.go # Main application file +├── go.mod # Go module file +└── README.md # This file +``` + +## Troubleshooting + +### Connection errors + +If you encounter connection errors, check: + +- Your internet connection +- The endpoint URL is accessible +- Firewall settings aren't blocking the connection + +### Module errors + +If you get "module not found" errors: + +```bash +go mod tidy +``` + +### History file permissions + +The app creates a history file at `/tmp/messenger_history.tmp`. If you encounter permission errors, ensure you have write access to `/tmp/`. + +## Dependencies + +- [github.com/chzyer/readline](https://github.com/chzyer/readline) - For input history and line editing + +## License + +This project is provided as-is for educational and development purposes. + +## Contributing + +Feel free to submit issues, fork the repository, and create pull requests for any improvements. diff --git a/terminal-messenger/application/go.mod b/terminal-messenger/application/go.mod new file mode 100644 index 0000000..0bf7c21 --- /dev/null +++ b/terminal-messenger/application/go.mod @@ -0,0 +1,8 @@ +module messenger + +go 1.24.6 + +require ( + github.com/chzyer/readline v1.5.1 // indirect + golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect +) diff --git a/terminal-messenger/application/go.sum b/terminal-messenger/application/go.sum new file mode 100644 index 0000000..2de3a80 --- /dev/null +++ b/terminal-messenger/application/go.sum @@ -0,0 +1,6 @@ +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/terminal-messenger/application/main.go b/terminal-messenger/application/main.go new file mode 100644 index 0000000..39f29d5 --- /dev/null +++ b/terminal-messenger/application/main.go @@ -0,0 +1,196 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/chzyer/readline" +) + +const ( + apiURL = "https://router.ivastudio.verint.live/ProxyScript/run/67bca862210071627d32ef12/current/basic_messenger" + colorReset = "\033[0m" + colorBlue = "\033[34m" + colorGreen = "\033[32m" +) + +type Metadata struct { + Channel string `json:"channel"` +} + +type Message struct { + Input string `json:"input"` + Model string `json:"model"` + PreviousResponseID string `json:"previous_response_id,omitempty"` + Metadata Metadata `json:"metadata"` +} + +type ContentItem struct { + Type string `json:"type"` + Text string `json:"text"` + Annotations []string `json:"annotations"` +} + +type OutputMessage struct { + Type string `json:"type"` + ID string `json:"id"` + Role string `json:"role"` + Content []ContentItem `json:"content"` +} + +type Response struct { + ID string `json:"id"` + Object string `json:"object"` + CreatedAt int64 `json:"created_at"` + Status string `json:"status"` + Model string `json:"model"` + Output []OutputMessage `json:"output"` +} + +type Messenger struct { + client *http.Client + model string + previousResponseID string +} + +func NewMessenger(model string) *Messenger { + return &Messenger{ + client: &http.Client{ + Timeout: 10 * time.Second, + }, + model: model, + } +} + +func (m *Messenger) sendRequest(input string) (*Response, error) { + msg := Message{ + Input: input, + Model: m.model, + PreviousResponseID: m.previousResponseID, + Metadata: Metadata{ + Channel: "text", + }, + } + + jsonData, err := json.Marshal(msg) + if err != nil { + return nil, fmt.Errorf("failed to marshal JSON: %w", err) + } + + req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := m.client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + var response Response + if err := json.Unmarshal(body, &response); err != nil { + return nil, fmt.Errorf("failed to unmarshal response: %w", err) + } + + // Update previous response ID for conversation continuity + if response.ID != "" { + m.previousResponseID = response.ID + } + + return &response, nil +} + +func (m *Messenger) SendMessage(text string) (string, error) { + resp, err := m.sendRequest(text) + if err != nil { + return "", err + } + + if resp.Status != "completed" { + return "", fmt.Errorf("response status: %s", resp.Status) + } + + // Extract text from the response output + if len(resp.Output) > 0 && len(resp.Output[0].Content) > 0 { + return resp.Output[0].Content[0].Text, nil + } + + return "", fmt.Errorf("no output received") +} + +func printHeader() { + fmt.Println("=== Terminal Messenger ===") + fmt.Println("Type your message (or 'quit' to exit)") + fmt.Println("Use ↑/↓ arrows to navigate history") + fmt.Println("==========================") + fmt.Println() +} + +func main() { + // Get model + fmt.Print("Enter model name (default: 'default'): ") + var model string + fmt.Scanln(&model) + + if model == "" { + model = "default" + } + + messenger := NewMessenger(model) + + printHeader() + + // Setup readline for input history and line editing + rl, err := readline.NewEx(&readline.Config{ + Prompt: colorBlue + "You: " + colorReset, + HistoryFile: "/tmp/messenger_history.tmp", + InterruptPrompt: "^C", + EOFPrompt: "exit", + }) + if err != nil { + fmt.Printf("Error setting up readline: %v\n", err) + return + } + defer rl.Close() + + // Main message loop + for { + input, err := rl.Readline() + if err != nil { // io.EOF or readline.ErrInterrupt + break + } + + input = strings.TrimSpace(input) + + if input == "quit" || input == "exit" { + break + } + + if input == "" { + continue + } + + response, err := messenger.SendMessage(input) + if err != nil { + fmt.Printf("Error: %v\n", err) + continue + } + + fmt.Printf(colorGreen+"Assistant: %s"+colorReset+"\n\n", response) + } + + fmt.Println("\nGoodbye!") +}