Skip to content
Open
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
19 changes: 19 additions & 0 deletions baml_language/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions baml_language/crates/bridge_go/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "bridge_go"
edition.workspace = true
version.workspace = true
authors.workspace = true
license.workspace = true

[lib]
name = "bridge_go"
crate-type = ["cdylib"]

[dependencies]
bex_events = { workspace = true }
bex_project = { workspace = true }
bridge_cffi = { workspace = true }
bridge_ctypes = { workspace = true }
sys_native = { workspace = true }
sys_types = { workspace = true }
futures = { workspace = true }
libc = { workspace = true }
log = { workspace = true }
once_cell = { workspace = true }
prost = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }

[package.metadata.ci]
wasm_support = false
30 changes: 30 additions & 0 deletions baml_language/crates/bridge_go/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROTO_DIR="${SCRIPT_DIR}/../bridge_ctypes/types"
OUT_DIR="${SCRIPT_DIR}/cffi/proto"

mkdir -p "${OUT_DIR}"

# Resolve protoc-gen-go
if [ -n "${PROTOC_GEN_GO_PATH:-}" ]; then
export PATH="$(dirname "${PROTOC_GEN_GO_PATH}"):${PATH}"
elif command -v mise &>/dev/null && mise which protoc-gen-go &>/dev/null; then
export PATH="$(dirname "$(mise which protoc-gen-go)")":${PATH}
fi
Comment on lines +11 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Declare and assign separately to avoid masking return values.

Shellcheck SC2155: When export and command substitution are combined, a non-zero exit from the subshell is masked by export's success.

🔧 Proposed fix
 # Resolve protoc-gen-go
 if [ -n "${PROTOC_GEN_GO_PATH:-}" ]; then
-    export PATH="$(dirname "${PROTOC_GEN_GO_PATH}"):${PATH}"
+    protoc_gen_go_dir="$(dirname "${PROTOC_GEN_GO_PATH}")"
+    export PATH="${protoc_gen_go_dir}:${PATH}"
 elif command -v mise &>/dev/null && mise which protoc-gen-go &>/dev/null; then
-    export PATH="$(dirname "$(mise which protoc-gen-go)")":${PATH}
+    mise_protoc_path="$(mise which protoc-gen-go)"
+    export PATH="$(dirname "${mise_protoc_path}"):${PATH}"
 fi
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 12-12: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 14-14: Declare and assign separately to avoid masking return values.

(SC2155)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@baml_language/crates/bridge_go/build.sh` around lines 11 - 15, The paired
export+command-substitution in build.sh masks command failures (SC2155); split
the assignment and export so failures surface: assign the dirname result to a
local variable (e.g., protoc_dir or mise_dir) using command substitution (for
both the PROTOC_GEN_GO_PATH branch and the mise branch that calls `mise which
protoc-gen-go`), check the command succeeded if needed, then export
PATH="$protoc_dir:$PATH" (or export PATH="$mise_dir:$PATH"); update the two
blocks around the export PATH lines to use these separate assignment-then-export
steps so non-zero exits are not masked.


# Verify protoc-gen-go is available
if ! command -v protoc-gen-go &>/dev/null; then
echo "ERROR: protoc-gen-go not found. Install with: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest" >&2
exit 1
fi

protoc \
--proto_path="${PROTO_DIR}" \
--go_out="${OUT_DIR}" \
--go_opt=paths=source_relative \
"${PROTO_DIR}/baml/cffi/v1/baml_inbound.proto" \
"${PROTO_DIR}/baml/cffi/v1/baml_outbound.proto"

echo "Generated Go proto files in ${OUT_DIR}/baml/cffi/v1/"
85 changes: 85 additions & 0 deletions baml_language/crates/bridge_go/cffi/bridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifndef BRIDGE_GO_H
#define BRIDGE_GO_H

#include <dlfcn.h>
#include <stdint.h>
#include <stdlib.h>

// Function pointer types
typedef struct { const int8_t *ptr; size_t len; } Buffer;
typedef Buffer (*VersionFn)(void);
typedef void* (*CreateBamlRuntimeFn)(const char *root_path, const char *src_files_json);
typedef void (*DestroyBamlRuntimeFn)(const void *runtime);
typedef void (*FreeBufferFn)(Buffer buf);
typedef void (*CallFunctionFn)(const char *function_name, const uint8_t *encoded_args, size_t length, uint32_t id);
typedef void (*CallbackFn)(uint32_t call_id, int32_t is_error, const int8_t *content, size_t length);
typedef void (*RegisterCallbackFn)(CallbackFn cb);
typedef uint64_t (*CloneHandleFn)(uint64_t key, int32_t handle_type);
typedef void (*ReleaseHandleFn)(uint64_t key, int32_t handle_type);
typedef int32_t (*CancelFunctionCallFn)(uint32_t id);
typedef void (*FlushEventsFn)(void);

// Static function pointers stored as void* to avoid type ambiguity
static void *versionFnPtr = NULL;
static void *createBamlRuntimeFnPtr = NULL;
static void *destroyBamlRuntimeFnPtr = NULL;
static void *freeBufferFnPtr = NULL;
static void *callFunctionFnPtr = NULL;
static void *registerCallbackFnPtr = NULL;
static void *cloneHandleFnPtr = NULL;
static void *releaseHandleFnPtr = NULL;
static void *cancelFunctionCallFnPtr = NULL;
static void *flushEventsFnPtr = NULL;

// Setters
static void setVersionFn(void *fn) { versionFnPtr = fn; }
static void setCreateBamlRuntimeFn(void *fn) { createBamlRuntimeFnPtr = fn; }
static void setDestroyBamlRuntimeFn(void *fn) { destroyBamlRuntimeFnPtr = fn; }
static void setFreeBufferFn(void *fn) { freeBufferFnPtr = fn; }
static void setCallFunctionFn(void *fn) { callFunctionFnPtr = fn; }
static void setRegisterCallbackFn(void *fn) { registerCallbackFnPtr = fn; }
static void setCloneHandleFn(void *fn) { cloneHandleFnPtr = fn; }
static void setReleaseHandleFn(void *fn) { releaseHandleFnPtr = fn; }
static void setCancelFunctionCallFn(void *fn) { cancelFunctionCallFnPtr = fn; }
static void setFlushEventsFn(void *fn) { flushEventsFnPtr = fn; }

// Wrappers — cast the void* to the correct function pointer type at call time
static Buffer wrapVersion(void) {
if (versionFnPtr) return ((VersionFn)versionFnPtr)();
return (Buffer){NULL, 0};
}
static void* wrapCreateBamlRuntime(const char *root_path, const char *src_files_json) {
if (createBamlRuntimeFnPtr) return ((CreateBamlRuntimeFn)createBamlRuntimeFnPtr)(root_path, src_files_json);
return NULL;
}
static void wrapDestroyBamlRuntime(const void *runtime) {
if (destroyBamlRuntimeFnPtr) ((DestroyBamlRuntimeFn)destroyBamlRuntimeFnPtr)(runtime);
}
static void wrapFreeBuffer(const int8_t *ptr, size_t len) {
if (freeBufferFnPtr) {
Buffer buf = {ptr, len};
((FreeBufferFn)freeBufferFnPtr)(buf);
}
}
static void wrapCallFunction(const char *function_name, const uint8_t *encoded_args, size_t length, uint32_t id) {
if (callFunctionFnPtr) ((CallFunctionFn)callFunctionFnPtr)(function_name, encoded_args, length, id);
}
static void wrapRegisterCallback(CallbackFn cb) {
if (registerCallbackFnPtr) ((RegisterCallbackFn)registerCallbackFnPtr)(cb);
}
static uint64_t wrapCloneHandle(uint64_t key, int32_t handle_type) {
if (cloneHandleFnPtr) return ((CloneHandleFn)cloneHandleFnPtr)(key, handle_type);
return 0;
}
static void wrapReleaseHandle(uint64_t key, int32_t handle_type) {
if (releaseHandleFnPtr) ((ReleaseHandleFn)releaseHandleFnPtr)(key, handle_type);
}
static int32_t wrapCancelFunctionCall(uint32_t id) {
if (cancelFunctionCallFnPtr) return ((CancelFunctionCallFn)cancelFunctionCallFnPtr)(id);
return 1;
}
static void wrapFlushEvents(void) {
if (flushEventsFnPtr) ((FlushEventsFn)flushEventsFnPtr)();
}

#endif // BRIDGE_GO_H
3 changes: 3 additions & 0 deletions baml_language/crates/bridge_go/cffi/exports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Package cffi provides low-level CGo wrappers for the bridge_go shared library.
// Use the pkg/ package for a higher-level Go API.
package cffi
163 changes: 163 additions & 0 deletions baml_language/crates/bridge_go/cffi/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package cffi

/*
#cgo LDFLAGS: -ldl
#include "bridge.h"
*/
import "C"
import (
"fmt"
"os"
"path/filepath"
"runtime"
"unsafe"
)

var libHandle unsafe.Pointer

// Init loads the bridge_go shared library and resolves all symbols.
// Call this from TestMain or package init.
func Init(libraryPath string) error {
cPath := C.CString(libraryPath)
defer C.free(unsafe.Pointer(cPath))

handle := C.dlopen(cPath, C.RTLD_LAZY|C.RTLD_LOCAL)
if handle == nil {
errStr := C.GoString(C.dlerror())
return fmt.Errorf("dlopen(%s): %s", libraryPath, errStr)
}
libHandle = handle

symbols := map[string]func(unsafe.Pointer){
"version": func(p unsafe.Pointer) { C.setVersionFn(p) },
"create_baml_runtime": func(p unsafe.Pointer) { C.setCreateBamlRuntimeFn(p) },
"destroy_baml_runtime": func(p unsafe.Pointer) { C.setDestroyBamlRuntimeFn(p) },
"free_buffer": func(p unsafe.Pointer) { C.setFreeBufferFn(p) },
"call_function": func(p unsafe.Pointer) { C.setCallFunctionFn(p) },
"register_callback": func(p unsafe.Pointer) { C.setRegisterCallbackFn(p) },
"cancel_function_call": func(p unsafe.Pointer) { C.setCancelFunctionCallFn(p) },
"clone_handle": func(p unsafe.Pointer) { C.setCloneHandleFn(p) },
"release_handle": func(p unsafe.Pointer) { C.setReleaseHandleFn(p) },
"flush_events": func(p unsafe.Pointer) { C.setFlushEventsFn(p) },
}
for name, setter := range symbols {
sym, err := resolveSymbol(handle, name)
if err != nil {
return fmt.Errorf("resolving %s: %w", name, err)
}
setter(sym)
}
return nil
Comment on lines +24 to +50
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Init can leave a partially-initialized loader state on failure.

At Line 29, libHandle is assigned before all symbols are resolved. If any resolution fails (Line 45-47), the function returns with an open handle and partially-installed function pointers.

💡 Suggested fix
 func Init(libraryPath string) error {
 	cPath := C.CString(libraryPath)
 	defer C.free(unsafe.Pointer(cPath))

 	handle := C.dlopen(cPath, C.RTLD_LAZY|C.RTLD_LOCAL)
 	if handle == nil {
 		errStr := C.GoString(C.dlerror())
 		return fmt.Errorf("dlopen(%s): %s", libraryPath, errStr)
 	}
-	libHandle = handle

 	symbols := map[string]func(unsafe.Pointer){
 		"version":              func(p unsafe.Pointer) { C.setVersionFn(p) },
 		"create_baml_runtime":  func(p unsafe.Pointer) { C.setCreateBamlRuntimeFn(p) },
 		"destroy_baml_runtime": func(p unsafe.Pointer) { C.setDestroyBamlRuntimeFn(p) },
 		"free_buffer":          func(p unsafe.Pointer) { C.setFreeBufferFn(p) },
 		"call_function":        func(p unsafe.Pointer) { C.setCallFunctionFn(p) },
 		"register_callback":    func(p unsafe.Pointer) { C.setRegisterCallbackFn(p) },
 		"cancel_function_call": func(p unsafe.Pointer) { C.setCancelFunctionCallFn(p) },
 		"clone_handle":         func(p unsafe.Pointer) { C.setCloneHandleFn(p) },
 		"release_handle":       func(p unsafe.Pointer) { C.setReleaseHandleFn(p) },
 		"flush_events":         func(p unsafe.Pointer) { C.setFlushEventsFn(p) },
 	}
 	for name, setter := range symbols {
 		sym, err := resolveSymbol(handle, name)
 		if err != nil {
+			C.dlclose(handle)
 			return fmt.Errorf("resolving %s: %w", name, err)
 		}
 		setter(sym)
 	}
+	libHandle = handle
 	return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
handle := C.dlopen(cPath, C.RTLD_LAZY|C.RTLD_LOCAL)
if handle == nil {
errStr := C.GoString(C.dlerror())
return fmt.Errorf("dlopen(%s): %s", libraryPath, errStr)
}
libHandle = handle
symbols := map[string]func(unsafe.Pointer){
"version": func(p unsafe.Pointer) { C.setVersionFn(p) },
"create_baml_runtime": func(p unsafe.Pointer) { C.setCreateBamlRuntimeFn(p) },
"destroy_baml_runtime": func(p unsafe.Pointer) { C.setDestroyBamlRuntimeFn(p) },
"free_buffer": func(p unsafe.Pointer) { C.setFreeBufferFn(p) },
"call_function": func(p unsafe.Pointer) { C.setCallFunctionFn(p) },
"register_callback": func(p unsafe.Pointer) { C.setRegisterCallbackFn(p) },
"cancel_function_call": func(p unsafe.Pointer) { C.setCancelFunctionCallFn(p) },
"clone_handle": func(p unsafe.Pointer) { C.setCloneHandleFn(p) },
"release_handle": func(p unsafe.Pointer) { C.setReleaseHandleFn(p) },
"flush_events": func(p unsafe.Pointer) { C.setFlushEventsFn(p) },
}
for name, setter := range symbols {
sym, err := resolveSymbol(handle, name)
if err != nil {
return fmt.Errorf("resolving %s: %w", name, err)
}
setter(sym)
}
return nil
handle := C.dlopen(cPath, C.RTLD_LAZY|C.RTLD_LOCAL)
if handle == nil {
errStr := C.GoString(C.dlerror())
return fmt.Errorf("dlopen(%s): %s", libraryPath, errStr)
}
symbols := map[string]func(unsafe.Pointer){
"version": func(p unsafe.Pointer) { C.setVersionFn(p) },
"create_baml_runtime": func(p unsafe.Pointer) { C.setCreateBamlRuntimeFn(p) },
"destroy_baml_runtime": func(p unsafe.Pointer) { C.setDestroyBamlRuntimeFn(p) },
"free_buffer": func(p unsafe.Pointer) { C.setFreeBufferFn(p) },
"call_function": func(p unsafe.Pointer) { C.setCallFunctionFn(p) },
"register_callback": func(p unsafe.Pointer) { C.setRegisterCallbackFn(p) },
"cancel_function_call": func(p unsafe.Pointer) { C.setCancelFunctionCallFn(p) },
"clone_handle": func(p unsafe.Pointer) { C.setCloneHandleFn(p) },
"release_handle": func(p unsafe.Pointer) { C.setReleaseHandleFn(p) },
"flush_events": func(p unsafe.Pointer) { C.setFlushEventsFn(p) },
}
for name, setter := range symbols {
sym, err := resolveSymbol(handle, name)
if err != nil {
C.dlclose(handle)
return fmt.Errorf("resolving %s: %w", name, err)
}
setter(sym)
}
libHandle = handle
return nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@baml_language/crates/bridge_go/cffi/lib.go` around lines 24 - 50, Init
assigns libHandle before resolving all symbols, so a failed resolveSymbol call
can return leaving an open handle and partially-set C.set* function pointers;
fix by moving the libHandle = handle assignment until after the symbols loop
completes successfully (i.e., only set libHandle when all resolveSymbol calls
succeed), or if you prefer keeping the early assignment, ensure you clean up on
any error by calling C.dlclose(handle) and undoing any setters (call the
C.setXXXFn(nil) setters for functions like C.setCreateBamlRuntimeFn,
C.setDestroyBamlRuntimeFn, C.setCallFunctionFn, etc.) before returning the
error.

}

func resolveSymbol(handle unsafe.Pointer, name string) (unsafe.Pointer, error) {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
C.dlerror() // clear
sym := C.dlsym(handle, cName)
if sym == nil {
errStr := C.GoString(C.dlerror())
return nil, fmt.Errorf("dlsym: %s", errStr)
}
return sym, nil
}

// FindLibrary locates the bridge_go shared library relative to the source tree.
func FindLibrary() (string, error) {
// Walk up from this source file to find the cargo target directory
_, thisFile, _, _ := runtime.Caller(0)
crateDir := filepath.Dir(filepath.Dir(thisFile)) // cffi/ -> bridge_go/
// Try common cargo output locations
var ext string
switch runtime.GOOS {
case "darwin":
ext = "dylib"
case "windows":
ext = "dll"
default:
ext = "so"
}
candidates := []string{
filepath.Join(crateDir, "..", "..", "target", "debug", fmt.Sprintf("libbridge_go.%s", ext)),
filepath.Join(crateDir, "..", "..", "target", "release", fmt.Sprintf("libbridge_go.%s", ext)),
}
Comment on lines +71 to +83
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For a Rust crate named bridge_gobuilt withcrate-type = ["cdylib"], what is the expected output filename on Windows? Is it bridge_go.dllorlibbridge_go.dll?

💡 Result:

For a Rust crate named bridge_go with crate-type = ["cdylib"], the expected output filename on Windows is bridge_go.dll.

Citations:


Windows library candidate name is incorrect.

On Windows, a Rust cdylib named bridge_go produces bridge_go.dll (no lib prefix), but the code always uses libbridge_go.<ext>. This will cause library loading to fail on Windows.

Suggested fix
-	var ext string
+	var libFile string
 	switch runtime.GOOS {
 	case "darwin":
-		ext = "dylib"
+		libFile = "libbridge_go.dylib"
 	case "windows":
-		ext = "dll"
+		libFile = "bridge_go.dll"
 	default:
-		ext = "so"
+		libFile = "libbridge_go.so"
 	}
 	candidates := []string{
-		filepath.Join(crateDir, "..", "..", "target", "debug", fmt.Sprintf("libbridge_go.%s", ext)),
-		filepath.Join(crateDir, "..", "..", "target", "release", fmt.Sprintf("libbridge_go.%s", ext)),
+		filepath.Join(crateDir, "..", "..", "target", "debug", libFile),
+		filepath.Join(crateDir, "..", "..", "target", "release", libFile),
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var ext string
switch runtime.GOOS {
case "darwin":
ext = "dylib"
case "windows":
ext = "dll"
default:
ext = "so"
}
candidates := []string{
filepath.Join(crateDir, "..", "..", "target", "debug", fmt.Sprintf("libbridge_go.%s", ext)),
filepath.Join(crateDir, "..", "..", "target", "release", fmt.Sprintf("libbridge_go.%s", ext)),
}
var libFile string
switch runtime.GOOS {
case "darwin":
libFile = "libbridge_go.dylib"
case "windows":
libFile = "bridge_go.dll"
default:
libFile = "libbridge_go.so"
}
candidates := []string{
filepath.Join(crateDir, "..", "..", "target", "debug", libFile),
filepath.Join(crateDir, "..", "..", "target", "release", libFile),
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@baml_language/crates/bridge_go/cffi/lib.go` around lines 71 - 83, The Windows
library candidate is built with a "lib" prefix which is incorrect; update the
candidate name construction around the switch on runtime.GOOS (where ext is set)
so that the filename prefix is platform-aware (use no "lib" prefix on Windows
and "lib" on other OSes) when creating the candidates slice (the filepath.Join
calls referencing crateDir and fmt.Sprintf("libbridge_go.%s", ext));
alternatively add a separate Windows-specific candidate "bridge_go.<ext>"
alongside the existing "libbridge_go.<ext>" entries so Windows can find
"bridge_go.dll".

for _, p := range candidates {
abs, _ := filepath.Abs(p)
if _, err := os.Stat(abs); err == nil {
return abs, nil
}
}
return "", fmt.Errorf("bridge_go shared library not found; tried: %v", candidates)
}

// Version returns the BAML engine version string.
func Version() string {
buf := C.wrapVersion()
if buf.ptr == nil || buf.len == 0 {
return ""
}
s := C.GoStringN((*C.char)(unsafe.Pointer(buf.ptr)), C.int(buf.len))
C.wrapFreeBuffer(buf.ptr, buf.len)
return s
}

// CreateBamlRuntime initializes the global BAML runtime from source files.
// srcFilesJSON is a JSON-encoded map[string]string of filename -> content.
// Returns an opaque runtime pointer (sentinel, not a real pointer).
func CreateBamlRuntime(rootPath, srcFilesJSON string) (unsafe.Pointer, error) {
cRootPath := C.CString(rootPath)
defer C.free(unsafe.Pointer(cRootPath))
cSrcFiles := C.CString(srcFilesJSON)
defer C.free(unsafe.Pointer(cSrcFiles))

ptr := C.wrapCreateBamlRuntime(cRootPath, cSrcFiles)
if ptr == nil {
return nil, fmt.Errorf("create_baml_runtime failed (returned null)")
}
return ptr, nil
}

// DestroyBamlRuntime is a no-op -- the runtime is global.
func DestroyBamlRuntime(ptr unsafe.Pointer) {
C.wrapDestroyBamlRuntime(ptr)
}

// RegisterCallback registers a single Go callback function pointer with Rust.
// cb must be a C-callable function pointer (//export function cast to unsafe.Pointer).
func RegisterCallback(cb unsafe.Pointer) {
C.wrapRegisterCallback((C.CallbackFn)(cb))
}

// CallFunction dispatches an async function call to Rust.
// Results and errors are delivered via the registered callback.
func CallFunction(functionName string, encodedArgs []byte, id uint32) {
cName := C.CString(functionName)
defer C.free(unsafe.Pointer(cName))

var cArgs *C.uint8_t
if len(encodedArgs) > 0 {
cArgs = (*C.uint8_t)(unsafe.Pointer(&encodedArgs[0]))
}

C.wrapCallFunction((*C.char)(unsafe.Pointer(cName)), cArgs, C.size_t(len(encodedArgs)), C.uint32_t(id))
}

// CancelFunctionCall signals the Rust engine to cancel an in-flight function call.
func CancelFunctionCall(id uint32) {
C.wrapCancelFunctionCall(C.uint32_t(id))
}
Comment on lines +146 to +148
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Propagate cancel result instead of discarding it.

wrapCancelFunctionCall returns a status from Rust, but Line 146-148 ignores it. This removes useful failure signaling for cancellation paths.

💡 Suggested fix
-func CancelFunctionCall(id uint32) {
-	C.wrapCancelFunctionCall(C.uint32_t(id))
+func CancelFunctionCall(id uint32) error {
+	if rc := C.wrapCancelFunctionCall(C.uint32_t(id)); rc != 0 {
+		return fmt.Errorf("cancel_function_call failed for id=%d", id)
+	}
+	return nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@baml_language/crates/bridge_go/cffi/lib.go` around lines 146 - 148, Change
CancelFunctionCall to capture and propagate the status returned by
wrapCancelFunctionCall instead of discarding it: call
C.wrapCancelFunctionCall(C.uint32_t(id)), store its return value, and return
that status to the caller (update CancelFunctionCall's signature to return the
appropriate Go type matching the C return, e.g., C.int or uint32 as used
elsewhere). Ensure callers of CancelFunctionCall are updated to handle the
returned status. Refer to symbols: CancelFunctionCall and
wrapCancelFunctionCall.


// CloneHandle clones an opaque handle, returning a new key.
func CloneHandle(key uint64, handleType int32) uint64 {
return uint64(C.wrapCloneHandle(C.uint64_t(key), C.int32_t(handleType)))
}

// ReleaseHandle releases an opaque handle.
func ReleaseHandle(key uint64, handleType int32) {
C.wrapReleaseHandle(C.uint64_t(key), C.int32_t(handleType))
}

// FlushEvents flushes the event sink.
func FlushEvents() {
C.wrapFlushEvents()
}
Loading
Loading