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 host function for direct logging #224

Merged
merged 2 commits into from
Jun 12, 2024
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 @@ -4,6 +4,7 @@

- Lowercase model name before invoking for hypermode hosted models [#221](https://github.com/gohypermode/runtime/pull/221)
- Improve HTTP error messages [#222](https://github.com/gohypermode/runtime/pull/222)
- Add host function for direct logging [#224](https://github.com/gohypermode/runtime/pull/224)

## 2024-06-03 - Version 0.8.2

Expand Down
14 changes: 9 additions & 5 deletions graphql/datasource/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ func (s Source) callFunction(ctx context.Context, callInfo callInfo) (any, []res
ctx = context.WithValue(ctx, utils.ExecutionIdContextKey, executionId)
ctx = context.WithValue(ctx, utils.PluginContextKey, info.Plugin)

// Create output buffers for the function to write logs to
// Also prepare a slice to capture log messages sent through the "log" host function.
messages := []utils.LogMessage{}
ctx = context.WithValue(ctx, utils.FunctionMessagesContextKey, &messages)

// Create output buffers for the function to write stdout/stderr to
buffers := utils.OutputBuffers{}

// Get a module instance for this request.
Expand All @@ -92,8 +96,9 @@ func (s Source) callFunction(ctx context.Context, callInfo callInfo) (any, []res
Buffers: buffers,
}

// Transform error lines in the output buffers to GraphQL errors
gqlErrors := transformErrors(buffers, callInfo)
// Transform messages (and error lines in the output buffers) to GraphQL errors
messages = append(messages, utils.TransformConsoleOutput(buffers)...)
gqlErrors := transformErrors(messages, callInfo)

return result, gqlErrors, err
}
Expand Down Expand Up @@ -238,8 +243,7 @@ func transformValue(data []byte, tf *fieldInfo) (result []byte, err error) {
return buf.Bytes(), nil
}

func transformErrors(buffers utils.OutputBuffers, ci callInfo) []resolve.GraphQLError {
messages := utils.TransformConsoleOutput(buffers)
func transformErrors(messages []utils.LogMessage, ci callInfo) []resolve.GraphQLError {
errors := make([]resolve.GraphQLError, 0, len(messages))
for _, msg := range messages {
// Only include errors. Other messages will be captured later and
Expand Down
1 change: 1 addition & 0 deletions hostfunctions/hostfns.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func Instantiate(ctx context.Context, runtime *wazero.Runtime) error {
b := (*runtime).NewHostModuleBuilder(hostModuleName)

// Each host function should get a line here:
b.NewFunctionBuilder().WithFunc(hostLog).Export("log")
b.NewFunctionBuilder().WithFunc(hostExecuteGQL).Export("executeGQL")
b.NewFunctionBuilder().WithFunc(hostInvokeClassifier).Export("invokeClassifier")
b.NewFunctionBuilder().WithFunc(hostComputeEmbedding).Export("computeEmbedding")
Expand Down
36 changes: 36 additions & 0 deletions hostfunctions/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024 Hypermode, Inc.
*/

package hostfunctions

import (
"context"
"hmruntime/logger"
"hmruntime/utils"

wasm "github.com/tetratelabs/wazero/api"
)

func hostLog(ctx context.Context, mod wasm.Module, pLevel uint32, pMessage uint32) {

var level, message string
err := readParams2(ctx, mod, pLevel, pMessage, &level, &message)
if err != nil {
logger.Err(ctx, err).Msg("Error reading input parameters.")
}

// write to the logger
logger.Get(ctx).
WithLevel(logger.ParseLevel(level)).
Str("text", message).
Bool("user_visible", true).
Msg("Message logged from function.")

// also store messages in the context, so we can return them to the caller
messages := ctx.Value(utils.FunctionMessagesContextKey).(*[]utils.LogMessage)
*messages = append(*messages, utils.LogMessage{
Level: level,
Message: message,
})
}
4 changes: 2 additions & 2 deletions logger/logwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (w logWriter) Write(p []byte) (n int, err error) {

func (w logWriter) logMessage(line string) {
l, message := utils.SplitConsoleOutputLine(line)
level := parseLevel(l)
level := ParseLevel(l)
if level == zerolog.NoLevel {
level = w.level
}
Expand All @@ -52,7 +52,7 @@ func (w logWriter) logMessage(line string) {
Msg("Message logged from function.")
}

func parseLevel(level string) zerolog.Level {
func ParseLevel(level string) zerolog.Level {
switch level {
case "debug":
return zerolog.DebugLevel
Expand Down
1 change: 1 addition & 0 deletions utils/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ type contextKey string
const ExecutionIdContextKey contextKey = "execution_id"
const PluginContextKey contextKey = "plugin"
const FunctionOutputContextKey contextKey = "function_output"
const FunctionMessagesContextKey contextKey = "function_messages"