Skip to content
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

Add Anthropic model interface to the Go SDK #493

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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ In previous releases, the name "Hypermode" was used for all three._
- Improve dev first use log messages [#489](https://github.com/hypermodeinc/modus/pull/489)
- Highlight endpoints when running in dev [#490](https://github.com/hypermodeinc/modus/pull/490)
- Fix data race in logging adapter [#491](https://github.com/hypermodeinc/modus/pull/491)
- Add Anthropic model interface to the Go SDK [#493](https://github.com/hypermodeinc/modus/pull/493)
- Move hyp settings for local model invocation to env variables [#495](https://github.com/hypermodeinc/modus/pull/495)
- Change GraphQL SDK examples to use a generic public GraphQL API [#501](https://github.com/hypermodeinc/modus/pull/501)

Expand Down
12 changes: 12 additions & 0 deletions sdk/go/examples/anthropic-functions/build.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@echo off

:: This build script works best for examples that are in this repository.
:: If you are using this as a template for your own project, you may need to modify this script,
:: to invoke the modus-go-build tool with the correct path to your project.

SET "PROJECTDIR=%~dp0"
pushd ..\..\tools\modus-go-build > nul
go run . "%PROJECTDIR%"
set "exit_code=%ERRORLEVEL%"
popd > nul
exit /b %exit_code%
12 changes: 12 additions & 0 deletions sdk/go/examples/anthropic-functions/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# This build script works best for examples that are in this repository.
# If you are using this as a template for your own project, you may need to modify this script,
# to invoke the modus-go-build tool with the correct path to your project.

PROJECTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
pushd ../../tools/modus-go-build > /dev/null
go run . "$PROJECTDIR"
exit_code=$?
popd > /dev/null
exit $exit_code
14 changes: 14 additions & 0 deletions sdk/go/examples/anthropic-functions/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module anthropic-functions-example

go 1.23.0

require github.com/hypermodeinc/modus/sdk/go v0.0.0

replace github.com/hypermodeinc/modus/sdk/go => ../../

require (
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
)
10 changes: 10 additions & 0 deletions sdk/go/examples/anthropic-functions/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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=
114 changes: 114 additions & 0 deletions sdk/go/examples/anthropic-functions/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* This example is part of the Modus project, licensed under the Apache License 2.0.
* You may modify and use this example in accordance with the license.
* See the LICENSE file that accompanied this code for further details.
*/

package main

import (
"encoding/json"
"fmt"
"strings"

"github.com/hypermodeinc/modus/sdk/go/pkg/http"
"github.com/hypermodeinc/modus/sdk/go/pkg/models"
"github.com/hypermodeinc/modus/sdk/go/pkg/models/anthropic"
)

type stockPriceInput struct {
Symbol string `json:"symbol"`
}

// This model name should match the one defined in the modus.json manifest file.
const modelName = "text-generator"

func GetStockPrice(company string, useTools bool) (string, error) {
model, err := models.GetModel[anthropic.MessagesModel](modelName)
if err != nil {
return "", err
}

input, err := model.CreateInput(
anthropic.NewUserMessage(
anthropic.StringContent(fmt.Sprintf("what is the stock price of %s?", company)),
),
)
if err != nil {
return "", err
}
// For Anthropic, system is passed as parameter to the invoke, not as a message
input.System = "You are a helpful assistant. Do not answer if you do not have up-to-date information."

// Optional parameters
input.Temperature = 1
input.MaxTokens = 100

if useTools {
input.Tools = []*anthropic.Tool{
{
Name: "stock_price",
InputSchema: `{"type":"object","properties":{"symbol":{"type":"string","description":"The stock symbol"}},"required":["symbol"]}`,
Description: "gets the stock price of a symbol",
},
}
input.ToolChoice = anthropic.ToolChoiceTool("stock_price")
}

// Here we invoke the model with the input we created.
output, err := model.Invoke(input)
if err != nil {
return "", err
}

if len(output.Content) != 1 {
return "", fmt.Errorf("unexpected output content length")
}
// If tools are not used, the output will be a text block
if output.Content[0].Type == "text" {
return strings.TrimSpace(*output.Content[0].Text), nil
}
if output.Content[0].Type != "tool_use" {
return "", fmt.Errorf("unexpected content type: %s", output.Content[0].Type)
}

toolUse := output.Content[0]
inputs := toolUse.Input

parsedInput := &stockPriceInput{}
if err := json.Unmarshal([]byte(*inputs), parsedInput); err != nil {
return "", err
}
symbol := parsedInput.Symbol
stockPrice, err := callStockPriceAPI(symbol)
if err != nil {
return "", err
}
return fmt.Sprintf("The stock price of %s is %s", symbol, stockPrice), nil
}

type stockPriceAPIResponse struct {
GlobalQuote struct {
Symbol string `json:"01. symbol"`
Price string `json:"05. price"`
} `json:"Global Quote"`
}

func callStockPriceAPI(symbol string) (string, error) {
url := fmt.Sprintf("https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=%s", symbol)
req := http.NewRequest(url)
resp, err := http.Fetch(req)
if err != nil {
return "", err
}
if !resp.Ok() {
return "", fmt.Errorf("HTTP request failed with status code %d", resp.Status)
}

data := &stockPriceAPIResponse{}
if err := json.Unmarshal(resp.Body, data); err != nil {
return "", err
}

return data.GlobalQuote.Price, nil
}
37 changes: 37 additions & 0 deletions sdk/go/examples/anthropic-functions/modus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "https://schema.hypermode.com/modus.json",
"endpoints": {
"default": {
"type": "graphql",
"path": "/graphql",
"auth": "bearer-token"
}
},
"models": {
"text-generator": {
"sourceModel": "claude-3-opus-20240229",
"connection": "anthropic",
"path": "v1/messages"
}
},
"connections": {
// This defines the Anthropic host, which is used by the model above.
// The {{API_KEY}} will be replaced by the secret provided in the Hypermode Console.
"anthropic": {
"type": "http",
"baseUrl": "https://api.anthropic.com/",
"headers": {
"x-api-key": "{{API_KEY}}",
"anthropic-version": "2023-06-01"
}
},
"stocks": {
"type": "http",
"baseUrl": "https://www.alphavantage.co/",
"queryParameters": {
// Get a free API key from https://www.alphavantage.co/support/#api-key
"apikey": "{{API_KEY}}"
}
}
}
}
Loading
Loading