10 | ├── .gitignore ├── internal └── pkg │ ├── web │ ├── headers.go │ ├── handler.go │ ├── response.go │ ├── templates.go │ └── web.go │ ├── websocketServer │ ├── websocketServer.go │ └── websocketServerImpl.go │ ├── chatSession │ ├── chatSession.go │ ├── chatSessionImpl.go │ └── agentChatSession.go │ ├── staticAssets │ └── handler.go │ ├── cookies │ ├── helpers.go │ ├── cookies.go │ └── cookies_test.go │ ├── sessions │ └── sessions.go │ ├── httpHandlers │ ├── uiTypes.go │ └── chatHandlers.go │ ├── config │ └── config.go │ ├── models │ ├── providers.go │ └── gemini │ │ └── gemini.go │ ├── mcpConfig │ └── config.go │ ├── agent │ └── agent.go │ └── tools │ └── mcp.go ├── web ├── templates │ ├── chat-messages.gohtml │ ├── chat-message.gohtml │ ├── chat-response.gohtml │ └── main.gohtml ├── frontend │ ├── public │ │ ├── logo.png │ │ └── logo-s.png │ ├── htmx.js │ ├── style.css │ ├── .gitignore │ ├── vite.config.js │ ├── css │ │ ├── header.css │ │ ├── footer.css │ │ ├── body.css │ │ ├── button.css │ │ ├── content.css │ │ └── loader.css │ ├── package.json │ ├── index.html │ └── main.js └── embed.go ├── configs └── mcp.config.json ├── cmd ├── calculator │ ├── applicationConfig.go │ └── main.go ├── ricky-bot │ ├── applicationConfig.go │ └── main.go └── calculator-mcp │ ├── README.md │ └── main.go ├── README.md ├── justfile ├── go.mod ├── docs └── architecture.md └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /ricky-bot* 3 | /calculator* 4 | -------------------------------------------------------------------------------- /internal/pkg/web/headers.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type Headers map[string]string 4 | -------------------------------------------------------------------------------- /web/templates/chat-messages.gohtml: -------------------------------------------------------------------------------- 1 | {{range .}} 2 | {{template "chat-message.gohtml" .}} 3 | {{end}} -------------------------------------------------------------------------------- /web/frontend/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apis/ai-chat/master/web/frontend/public/logo.png -------------------------------------------------------------------------------- /web/frontend/public/logo-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apis/ai-chat/master/web/frontend/public/logo-s.png -------------------------------------------------------------------------------- /configs/mcp.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "calculator": { 4 | "command": "/home/apis/sandbox/ai-chat/calculator-mcp", 5 | "args": [] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /web/embed.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import "embed" 4 | 5 | var ( 6 | //go:embed templates 7 | TemplateFS embed.FS 8 | 9 | //go:embed frontend/dist 10 | EmbedFs embed.FS 11 | ) 12 | -------------------------------------------------------------------------------- /web/frontend/htmx.js: -------------------------------------------------------------------------------- 1 | // Workaround to enable HTMX extensions support with Vite, see https://github.com/bigskysoftware/htmx/issues/1690 2 | 3 | import htmx from 'htmx.org' 4 | window.htmx = htmx -------------------------------------------------------------------------------- /web/frontend/style.css: -------------------------------------------------------------------------------- 1 | @import "css/body.css"; 2 | @import "css/loader.css"; 3 | @import "css/button.css"; 4 | @import "css/header.css"; 5 | @import "css/content.css"; 6 | @import "css/footer.css"; 7 | -------------------------------------------------------------------------------- /cmd/calculator/applicationConfig.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type applicationConfig struct { 4 | Host string `config_default:"localhost" config_description:"Server host interface"` 5 | Port int `config_default:"8081" config_description:"Server port"` 6 | } 7 | -------------------------------------------------------------------------------- /internal/pkg/websocketServer/websocketServer.go: -------------------------------------------------------------------------------- 1 | package websocketServer 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | "net/http" 6 | ) 7 | 8 | type WebsocketServer interface { 9 | Handler(responseWriter http.ResponseWriter, request *http.Request) 10 | Publish(id uuid.UUID, message []byte) 11 | } 12 | -------------------------------------------------------------------------------- /web/templates/chat-message.gohtml: -------------------------------------------------------------------------------- 1 |
7 | 8 | -------------------------------------------------------------------------------- /web/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /internal/pkg/web/handler.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | type Handler struct { 8 | Request func(request *http.Request, simulatedDelay int) *Response 9 | SimulatedDelay int 10 | } 11 | 12 | func (instance Handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) { 13 | response := instance.Request(request, instance.SimulatedDelay) 14 | response.Write(responseWriter) 15 | } 16 | -------------------------------------------------------------------------------- /web/frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import cssnano from "cssnano" 2 | import postcssImport from "postcss-import" 3 | import postcssInherit from "postcss-inherit" 4 | 5 | 6 | export default { 7 | base: "/chat", 8 | css: { 9 | postcss: { 10 | plugins: [ 11 | cssnano({preset: "default"}), 12 | postcssImport(), 13 | postcssInherit(), 14 | ], 15 | } 16 | }, 17 | } -------------------------------------------------------------------------------- /internal/pkg/chatSession/chatSession.go: -------------------------------------------------------------------------------- 1 | package chatSession 2 | 3 | type ChatBlockResponse struct { 4 | ChatBlock ChatBlock 5 | New bool 6 | } 7 | 8 | type ChatBlock struct { 9 | SystemMessage string 10 | UserMessage string 11 | AssistantMessage string 12 | Completed bool 13 | Failed bool 14 | } 15 | 16 | type ChatSession interface { 17 | EnqueueMessage(message string) error 18 | Shutdown() 19 | ChatBlocks() []ChatBlock 20 | } 21 | 22 | type ChatBlockResponseFunc func(response ChatBlockResponse) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI Chat Project 2 | 3 | #### TODO items 4 | 5 | * Tools integration 6 | * Sessions type locking 7 | * Session timeout and scheduled cleanup 8 | * Websocket behavior after time out 9 | * Incremental update from server 10 | * Disable controls when server is answering 11 | * Cancel answer button 12 | * "Answering" UI spinner 13 | * Format user input as text - preserve new lines / spaces 14 | 15 | 16 | curl -LsSf https://astral.sh/uv/install.sh | sh 17 | 18 | mcphost -m ollama:granite3.3:8b --config "./configs/mcphost.config.json" 19 | 20 | ./mcphost -m "ollama:qwen3:8b" --config "../ai-chat/configs/mcphost.config.json" -------------------------------------------------------------------------------- /web/frontend/css/header.css: -------------------------------------------------------------------------------- 1 | .main-header { 2 | color: var(--colorHeadline); 3 | background-color: var(--colorBackground); 4 | 5 | display: flex; 6 | flex-direction: row-reverse; 7 | align-items: center; 8 | height: 86px; 9 | align-content: center; 10 | } 11 | 12 | .page-title-image { 13 | flex: 0 74px; 14 | } 15 | 16 | .page-title-text { 17 | flex: 1; 18 | } 19 | 20 | /*.page-image {*/ 21 | /* height: 64px;*/ 22 | /* width: 64px;*/ 23 | /*}*/ 24 | 25 | .page-title { 26 | font-size: 2.5rem; 27 | font-weight: 600; 28 | } 29 | 30 | .page-subtitle { 31 | font-size: 1.5rem; 32 | font-weight: 600; 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /web/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "htmx", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "cssnano": "^7.0.7", 13 | "postcss": "^8.5.6", 14 | "postcss-import": "^16.1.1", 15 | "postcss-inherit": "^4.1.0", 16 | "vite": "^6.3.5" 17 | }, 18 | "dependencies": { 19 | "@fontsource/noto-sans": "^5.2.7", 20 | "@fontsource/noto-sans-mono": "^5.2.7", 21 | "htmx.org": "^2.0.5", 22 | "katex": "^0.16.22", 23 | "marked": "^15.0.12", 24 | "marked-katex-extension": "^5.1.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /web/templates/chat-response.gohtml: -------------------------------------------------------------------------------- 1 | {{if eq .New true}} 2 |
10 |