Skip to content

Raphael/chat go #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1a50e2f
Update settings.json
rderbier Mar 24, 2025
6740982
Create .gitignore
rderbier Mar 24, 2025
639001c
go mod update
rderbier Mar 24, 2025
03a02d4
Create modus.json
rderbier Mar 24, 2025
4973a50
update API
rderbier Mar 24, 2025
b752a2e
add hypermot-chat
rderbier May 14, 2025
12ca472
Merge branch 'main' into raphael/chat-go
rderbier May 14, 2025
8ed9d29
use model router
rderbier May 15, 2025
e6ebb51
add working chatAgent logic
rderbier May 23, 2025
31fd70f
chat agent connected to Dgraph
rderbier May 27, 2025
2ad54a3
Merge branch 'main' into raphael/chat-go
danstarns May 30, 2025
aec52ad
restore img
danstarns May 30, 2025
449f7b1
remove pnpm lock
danstarns May 30, 2025
c83315b
trunk fixes
danstarns May 30, 2025
61a9a55
custom adapter to talk to backend
danstarns May 30, 2025
6b0ef12
remove unused
danstarns May 30, 2025
07fc75b
mod tidy
danstarns May 30, 2025
0c17d95
trunk fixes
danstarns May 30, 2025
e5100f0
chatkit alpha.4
danstarns May 30, 2025
2725b78
docs: *
danstarns May 30, 2025
e42d060
Merge branch 'main' into raphael/chat-go
rderbier Jun 2, 2025
9a08826
Merge branch 'raphael/chat-go' of github.com:hypermodeinc/modus-recip…
rderbier Jun 2, 2025
50e7bca
update chat agent
rderbier Jun 2, 2025
6ac5461
update logic and schema
rderbier Jun 6, 2025
1b50b98
add makefile for local
rderbier Jun 11, 2025
aac801a
remove unused SessionID
rderbier Jun 11, 2025
1a7b9e7
update to latest modus alpha release to resolve agent start error
johnymontana Jun 11, 2025
c722db9
add username and use alpha.7
rderbier Jun 13, 2025
44b643b
Merge branch 'raphael/chat-go' of github.com:hypermodeinc/modus-recip…
rderbier Jun 13, 2025
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
97 changes: 97 additions & 0 deletions chat-agent/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# usage example
# make ENV=local schema-gql
# make drop-data

# Default environment 'local' - other env 'hypermode'
DOCKER_INSTANCE_NAME ?= dgraph-chat # Set a default Docker instance name or get it from an environment variable


# .env.hypermode file with the following variables
# DGRAPH_CONNECTION_STRING=dgraph://agent-context-havona-tech-ltd.hypermode.host:443?sslmode=verify-ca&bearertoken=<token>

# you can copy paste the connection string from the Hypermode console

# Load the .env file based on the environment
ifneq (,$(wildcard .env))
echo "Using .env file"
include .env
export $(shell sed 's/=.*//' .env)
DGRAPH_HOST_PORT = $(shell echo '$(DGRAPH_CONNECTION_STRING)' | sed -E 's|^[a-z]+://([^/?]+).*|\1|')
DGRAPH_ENDPOINT = $(shell if [[ $(DGRAPH_HOST_PORT) == localhost* ]]; then echo "http://$(DGRAPH_HOST_PORT)"; else echo "https://$(DGRAPH_HOST_PORT)/dgraph"; fi)
DGRAPH_API_TOKEN := $(shell echo '$(DGRAPH_CONNECTION_STRING)' | sed -E 's/.*[?&]bearertoken=([^&]+).*/\1/')
else
DGRAPH_ENDPOINT = http://localhost:8080
endif



## define DGRAPH_API_TOKEN in env
## define API_TOKEN in env

current_dir = $(shell pwd)

DGRAPH_VERSION = latest

help: ## Print target help
@echo "Usage: make <target> or make <target> ENV=<environment>"
@echo "Targets:"
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m %-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
backend:
cd ./api-go && modus dev &
local: env up schema-dql ## start dgraph, modus API and hypermode-chat frontend (local development)
cd ./api-go && modus dev & echo $$! > $(current_dir)/.modus_pid
cd ../hypermode-chat && npm install && npm run dev & echo $$! > $(current_dir)/.npm_pid
stop: ## Stop the backend and frontend (local development)
@if [ -f $(current_dir)/.modus_pid ]; then \
kill -TERM -- `cat $(current_dir)/.modus_pid`; \
rm $(current_dir)/.modus_pid; \
fi
@if [ -f $(current_dir)/.npm_pid ]; then \
kill -TERM -- `cat $(current_dir)/.npm_pid`; \
rm $(current_dir)/.npm_pid; \
fi
ui: ## Start the UI (local development)
@echo "Starting UI at http://localhost:3000"
cd ../hypermode-chat && npm install && npm run dev &
up: ## Init and Start dgraph/standalone container (local development)
@if [ -z "$$(docker ps -q -f name=$(DOCKER_INSTANCE_NAME))" ]; then \
if [ -z "$$(docker ps -aq -f name=$(DOCKER_INSTANCE_NAME))" ]; then \
docker run -d --name $(DOCKER_INSTANCE_NAME) -p "8080:8080" -p "9080:9080" dgraph/standalone:$(DGRAPH_VERSION); \
else \
docker start $(DOCKER_INSTANCE_NAME); \
fi \
else \
echo "Container $(DOCKER_INSTANCE_NAME) is already running."; \
fi
start: ## Start dgraph/standalone container (local development) after you stopped it
docker start dgraph-rag
down: ## Stop the container (local development)
docker stop dgraph-rag
env: ## update the .env file with a user-provided token
@if [ ! -f ./api-go/.env ]; then \
read -r -p "Enter model router token: " token; \
if grep -q "MODUS_HYPERMODE_ROUTER_TOKEN=" ./api-go/.env 2>/dev/null; then \
sed -i '' "s|MODUS_HYPERMODE_ROUTER_TOKEN=.*|MODUS_HYPERMODE_ROUTER_TOKEN=$$token|" ./api-go/.env; \
else \
echo "MODUS_HYPERMODE_ROUTER_TOKEN=$$token" >> ./api-go/.env; \
fi; \
echo "Updated ./api-go/.env with MODUS_HYPERMODE_ROUTER_TOKEN"; \
fi

echo: ## Print the current environment
@echo "DGRAPH_ENDPOINT: $(DGRAPH_ENDPOINT)"


drop-data: ## Drops all data (but not the schema, local development)
@curl -X POST -H 'Authorization: Bearer $(DGRAPH_API_TOKEN)' $(DGRAPH_ENDPOINT)/alter -d '{"drop_op": "DATA"}'

drop-all: ## Drops data and schema (local development)
@curl -X POST -H 'Authorization: Bearer $(DGRAPH_API_TOKEN)' $(DGRAPH_ENDPOINT)/alter -d '{"drop_all": true}'


schema-dql: ## Load/update a DQL schema
ifneq (,$(wildcard ./schema.dql))
@curl --data-binary '@./schema.dql' -H 'content-type: application/dql' -H 'Authorization: Bearer $(DGRAPH_API_TOKEN)' $(DGRAPH_ENDPOINT)/alter
else
@echo "No schema.dql found"
endif
70 changes: 70 additions & 0 deletions chat-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Chat Agent with KG

## Launch the API, the frontend and Dgraph

### Run locally

```shell
make local
```

### Run the API locally with an Hypermode Graph instance (hosted)

Update or create `.env` file with

```sh
DGRAPH_CONNECTION_STRING=<Hypermode Graph connection string>
```

The connection string looks like
`dgraph://modus-recipes-backend-hypermode.hypermode.host:443?sslmode=verify-ca&bearertoken=...`

Update api-go/modus.json

```json
"dgraph": {
"type": "dgraph",
"connString": "dgraph://<...>.hypermode.host:443?sslmode=verify-ca&bearertoken={{API_KEY}}"
}
```

and set the MODUS_DGRAPH_API_KEY value in api-go/.env file

## Testing

Open http://localhost:3000/ in your Browser and start a conversation.

Enter some fact like

```txt
I put my passport in the red box under my bed.

I met with Will at the AI Conference yesterday.

Will is working at Hypermode.

...
```

You can see the KG in Dgraph

```txt
{
fact(func:has(fact)) {
name:fact
created_at
happened_on
entity:fact.entity { name:entity.name type:entity.type}
}
}
```

and ask questions in the conversation.

```txt
Where did I put my passport?

What do you know about Will ?

...
```
16 changes: 16 additions & 0 deletions chat-agent/api-go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Ignore macOS system files
.DS_Store

# Ignore environment variable files
.env
.env.*

# Ignore build output directories
build/

# Ignore Go debuger and generated files
__debug_bin*
*_generated.go
*.generated.go

.modusdb/
85 changes: 85 additions & 0 deletions chat-agent/api-go/chatAgent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import (
"encoding/json"
"fmt"

"github.com/hypermodeinc/modus/sdk/go/pkg/agents"
"github.com/hypermodeinc/modus/sdk/go/pkg/models"
"github.com/hypermodeinc/modus/sdk/go/pkg/models/openai"
)

const (
MAX_HISTORY = 10
MODEL_NAME = "text-generator"
)

type ChatAgent struct {
agents.AgentBase
state ChatAgentState
}

type ChatAgentState struct {
ChatHistory string
}

func (c *ChatAgent) Name() string {
return "Chat-v1"
}

func (c *ChatAgent) GetState() *string {
serializedState, _ := json.Marshal(c.state)
serializedStateStr := string(serializedState)
return &serializedStateStr
}

func (c *ChatAgent) SetState(data *string) {
err := json.Unmarshal([]byte(*data), &c.state)
if err != nil {
fmt.Println("Error unmarshalling state:", err)
}
}

func (c *ChatAgent) OnStart() error {
c.state.ChatHistory = ""
fmt.Println("Agent started")
return nil
}

func (c *ChatAgent) OnReceiveMessage(msgName string, data *string) (*string, error) {
switch msgName {
case "new_user_message":
return c.chat(data)
case "get_chat_history":
return &c.state.ChatHistory, nil
default:
return nil, nil
}
}

func (c *ChatAgent) chat(data *string) (*string, error) {
model, _ := models.GetModel[openai.ChatModel](MODEL_NAME)
loopLimit := 3

llmResponse := llmWithTools(
model,
chatTools(),
systemPrompt(),
*data,
c.state.ChatHistory,
openai.ResponseFormatText,
executeToolCall,
loopLimit,
MAX_HISTORY,
)

c.state.ChatHistory = llmResponse.ChatHistory
fmt.Println(llmResponse.Response)

return &llmResponse.Response, nil
}

func (c *ChatAgent) DeleteAgent(id string) error {
_, err := agents.Stop(id)
return err
}
14 changes: 14 additions & 0 deletions chat-agent/api-go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module api-go

go 1.24.2

require (
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.7
github.com/tidwall/gjson v1.18.0
)

require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
)
16 changes: 16 additions & 0 deletions chat-agent/api-go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.5 h1:Zl9f9ssdyCs/EuNiLDXyoLpwBM9upIYIfJvS0BQuFQ8=
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.5/go.mod h1:WX/57csiUC+LYaROkVo0bIPiG8jkQRxwD4Ajb7a0s6A=
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.6 h1:8fZkA5lORW5ayf4EUtWKLhpOPJ63s9n+dThWSLzPuvY=
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.6/go.mod h1:mIHmDGQb2UDCT/9McFbHp3jdvv07EIyeGtqHOCVVkqo=
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.7 h1:q19+G8lc6thQNHTWin0i1zbz8GmGBg6z4Exb3rLZuMQ=
github.com/hypermodeinc/modus/sdk/go v0.18.0-alpha.7/go.mod h1:mIHmDGQb2UDCT/9McFbHp3jdvv07EIyeGtqHOCVVkqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
58 changes: 58 additions & 0 deletions chat-agent/api-go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"github.com/hypermodeinc/modus/sdk/go/pkg/agents"
)

type SearchResponse struct {
Message string `json:"message"`
History string `json:"history"`
User_preferences string `json:"user_preferences"`
}

var session_ID = ""

func init() {
agents.Register(&ChatAgent{})
}

// The following are regular Modus functions.

func CreateConversation() (id *string, err error) {
// ChatAgent Name is "Chat-v1"
// A Conversation is an instance of the ChatAgent.
info, err := agents.Start("Chat-v1")
if err != nil {
return nil, err
}

return &info.Id, nil
}
func ContinueChat(id string, query string) (*string, error) {
// Send a message to the agent in charge of the conversation.
response, err := agents.SendMessage(id, "new_user_message", agents.WithData(query))
if err != nil {
return nil, err
}

return response, nil
}

func ChatHistory(id string) (*string, error) {
// Send a message to the agent and get a response.
return agents.SendMessage(id, "get_chat_history")
}

/*func SaveFact(id string, fact string, location string) (*string, error) {
// Send a message to the agent to save a fact.
return (save_fact(id, fact, location))
}
*/

func DeleteAgent(id string) (*string, error) {
_, err := agents.Stop(id)
if err != nil {
return nil, err
}
return &id, nil
}
30 changes: 30 additions & 0 deletions chat-agent/api-go/modus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://schema.hypermode.com/modus.json",
"endpoints": {
"default": {
"type": "graphql",
"path": "/graphql",
"auth": "bearer-token"
}
},
"models": {
"text-generator": {
"sourceModel": "gpt-4o",
"connection": "hypermode-router",
"path": "v1/chat/completions"
}
},
"connections": {
"hypermode-router": {
"type": "http",
"baseUrl": "https://models.hypermode.host/",
"headers": {
"Authorization": "Bearer {{TOKEN}}"
}
},
"dgraph": {
"type": "dgraph",
"connString": "dgraph://localhost:9080"
}
}
}
Loading