package main /*#include <stdint.h>#include <stdlib.h> typedef struct { void* ptr; size_t len;} cliproxy_buffer; typedef int (*cliproxy_host_call_fn)(void*, const char*, const uint8_t*, size_t, cliproxy_buffer*);typedef void (*cliproxy_host_free_fn)(void*, size_t); typedef struct { uint32_t abi_version; void* host_ctx; cliproxy_host_call_fn call; cliproxy_host_free_fn free_buffer;} cliproxy_host_api; typedef int (*cliproxy_plugin_call_fn)(char*, uint8_t*, size_t, cliproxy_buffer*);typedef void (*cliproxy_plugin_free_fn)(void*, size_t);typedef void (*cliproxy_plugin_shutdown_fn)(void); typedef struct { uint32_t abi_version; cliproxy_plugin_call_fn call; cliproxy_plugin_free_fn free_buffer; cliproxy_plugin_shutdown_fn shutdown;} cliproxy_plugin_api; extern int cliproxyPluginCall(char*, uint8_t*, size_t, cliproxy_buffer*);extern void cliproxyPluginFree(void*, size_t);extern void cliproxyPluginShutdown(void); static const cliproxy_host_api* stored_host; static void store_host_api(const cliproxy_host_api* host) { stored_host = host;} static int call_host_api(const char* method, const uint8_t* request, size_t request_len, cliproxy_buffer* response) { if (stored_host == NULL || stored_host->call == NULL) { return 1; } return stored_host->call(stored_host->host_ctx, method, request, request_len, response);} static void free_host_buffer(void* ptr, size_t len) { if (stored_host != NULL && stored_host->free_buffer != NULL && ptr != NULL) { stored_host->free_buffer(ptr, len); }}*/import "C" import ( "bytes" "encoding/json" "fmt" "html" "net/http" "net/url" "strconv" "strings" "unsafe" "github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginabi" "github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginapi") const ( defaultModel = "gpt-5.5" defaultPrompt = "Summarize host model callbacks in one short sentence." pluginName = "host-model-callback" resourcePath = "/status" resourceContentType = "text/html; charset=utf-8") type envelope struct { OK bool `json:"ok"` Result json.RawMessage `json:"result,omitempty"` Error *envelopeError `json:"error,omitempty"`} type envelopeError struct { Code string `json:"code"` Message string `json:"message"`} type registration struct { SchemaVersion uint32 `json:"schema_version"` Metadata pluginapi.Metadata `json:"metadata"` Capabilities registrationCapabilities `json:"capabilities"`} type registrationCapabilities struct { ManagementAPI bool `json:"management_api"`} type managementRegistration struct { Resources []managementResource `json:"resources,omitempty"`} type managementResource struct { Path string `json:"Path"` Menu string `json:"Menu"` Description string `json:"Description"`} type managementRequest struct { Method string Path string Headers http.Header Query url.Values Body []byte HostCallbackID string `json:"host_callback_id,omitempty"`} type managementResponse struct { StatusCode int `json:"StatusCode"` Headers http.Header `json:"Headers"` Body []byte `json:"Body"`} type managementBodyOptions struct { Model string `json:"model"` Mode string `json:"mode"` EntryProtocol string `json:"entry_protocol"` ExitProtocol string `json:"exit_protocol"` Prompt string `json:"prompt"` Stream *bool `json:"stream"` Body json.RawMessage `json:"body"` Headers http.Header `json:"headers"` Query url.Values `json:"query"` Alt string `json:"alt"` ImplicitClose *bool `json:"implicit_close"`} type hostModelExecutionRequest struct { pluginapi.HostModelExecutionRequest HostCallbackID string `json:"host_callback_id,omitempty"`} type runOptions struct { Model string Mode string EntryProtocol string ExitProtocol string Prompt string Stream bool Body []byte Headers http.Header Query url.Values Alt string ImplicitClose bool HostCallbackID string} type chatCompletionRequest struct { Model string `json:"model"` Stream bool `json:"stream"` Messages []chatMessage `json:"messages"`} type chatMessage struct { Role string `json:"role"` Content string `json:"content"`} type streamPageData struct { StatusCode int Headers http.Header StreamID string Chunks []string Error string CloseMode string CloseError string} func main() {} //export cliproxy_plugin_initfunc cliproxy_plugin_init(host *C.cliproxy_host_api, plugin *C.cliproxy_plugin_api) C.int { if plugin == nil { return 1 } C.store_host_api(host) plugin.abi_version = C.uint32_t(pluginabi.ABIVersion) plugin.call = C.cliproxy_plugin_call_fn(C.cliproxyPluginCall) plugin.free_buffer = C.cliproxy_plugin_free_fn(C.cliproxyPluginFree) plugin.shutdown = C.cliproxy_plugin_shutdown_fn(C.cliproxyPluginShutdown) return 0} //export cliproxyPluginCallfunc cliproxyPluginCall(method *C.char, request *C.uint8_t, requestLen C.size_t, response *C.cliproxy_buffer) C.int { if response != nil { response.ptr = nil response.len = 0 } if method == nil { writeResponse(response, errorEnvelope("invalid_method", "method is required")) return 1 } var requestBytes []byte if request != nil && requestLen > 0 { requestBytes = C.GoBytes(unsafe.Pointer(request), C.int(requestLen)) } raw, errHandle := handleMethod(C.GoString(method), requestBytes) if errHandle != nil { writeResponse(response, errorEnvelope("plugin_error", errHandle.Error())) return 1 } writeResponse(response, raw) return 0} //export cliproxyPluginFreefunc cliproxyPluginFree(ptr unsafe.Pointer, len C.size_t) { if ptr != nil { C.free(ptr) } _ = len} //export cliproxyPluginShutdownfunc cliproxyPluginShutdown() {} func handleMethod(method string, request []byte) ([]byte, error) { switch method { case pluginabi.MethodPluginRegister, pluginabi.MethodPluginReconfigure: return okEnvelope(pluginRegistration()) case pluginabi.MethodManagementRegister: return okEnvelope(managementRegistration{ Resources: []managementResource{{ Path: resourcePath, Menu: "Host Model Callback", Description: "Runs a model request through host.model callbacks and displays the result.", }}, }) case pluginabi.MethodManagementHandle: return handleManagement(request) default: return errorEnvelope("unknown_method", "unknown method: "+method), nil }} func pluginRegistration() registration { return registration{ SchemaVersion: pluginabi.SchemaVersion, Metadata: pluginapi.Metadata{ Name: pluginName, Version: "0.1.0", Author: "router-for-me", GitHubRepository: "https://github.com/router-for-me/CLIProxyAPI", Logo: "https://raw.githubusercontent.com/router-for-me/CLIProxyAPI/main/docs/logo.png", ConfigFields: []pluginapi.ConfigField{}, }, Capabilities: registrationCapabilities{ ManagementAPI: true, }, }} func handleManagement(raw []byte) ([]byte, error) { var req managementRequest if len(raw) > 0 { if errUnmarshal := json.Unmarshal(raw, &req); errUnmarshal != nil { return nil, fmt.Errorf("decode management request: %w", errUnmarshal) } } opts, errOptions := optionsFromManagementRequest(req) if errOptions != nil { page := renderPage(opts, 0, nil, nil, nil, errOptions.Error(), "", "") return okEnvelope(htmlResponse(http.StatusBadRequest, page)) } if opts.Stream { data := executeStream(opts) page := renderPage(opts, data.StatusCode, data.Headers, nil, data.Chunks, data.Error, data.CloseMode, data.CloseError) return okEnvelope(htmlResponse(http.StatusOK, page)) } resp, errExecute := executeOnce(opts) if errExecute != nil { page := renderPage(opts, 0, nil, nil, nil, errExecute.Error(), "", "") return okEnvelope(htmlResponse(http.StatusOK, page)) } page := renderPage(opts, resp.StatusCode, resp.Headers, resp.Body, nil, "", "", "") return okEnvelope(htmlResponse(http.StatusOK, page))} func optionsFromManagementRequest(req managementRequest) (runOptions, error) { opts := runOptions{ Model: defaultModel, Mode: "non-stream", EntryProtocol: "openai", ExitProtocol: "openai", Prompt: defaultPrompt, Headers: http.Header{}, Query: url.Values{}, } opts.HostCallbackID = strings.TrimSpace(req.HostCallbackID) if len(req.Body) > 0 { if errApplyBody := applyBodyOptions(&opts, req.Body); errApplyBody != nil { return opts, errApplyBody } } if errApplyQuery := applyQueryOptions(&opts, req.Query); errApplyQuery != nil { return opts, errApplyQuery } if opts.Stream { opts.Mode = "stream" } else { opts.Mode = "non-stream" } return opts, nil} func applyBodyOptions(opts *runOptions, raw []byte) error { var bodyOpts managementBodyOptions if errUnmarshal := json.Unmarshal(raw, &bodyOpts); errUnmarshal != nil { return fmt.Errorf("decode JSON request body: %w", errUnmarshal) } if strings.TrimSpace(bodyOpts.Model) != "" { opts.Model = strings.TrimSpace(bodyOpts.Model) } if strings.TrimSpace(bodyOpts.Mode) != "" { applyMode(opts, bodyOpts.Mode) } if strings.TrimSpace(bodyOpts.EntryProtocol) != "" { opts.EntryProtocol = strings.TrimSpace(bodyOpts.EntryProtocol) } if strings.TrimSpace(bodyOpts.ExitProtocol) != "" { opts.ExitProtocol = strings.TrimSpace(bodyOpts.ExitProtocol) } if bodyOpts.Prompt != "" { opts.Prompt = bodyOpts.Prompt } if bodyOpts.Stream != nil { opts.Stream = *bodyOpts.Stream } if len(bodyOpts.Body) > 0 && string(bodyOpts.Body) != "null" { if !json.Valid(bodyOpts.Body) { return fmt.Errorf("body must be valid JSON") } opts.Body = append([]byte(nil), bodyOpts.Body...) } if bodyOpts.Headers != nil { opts.Headers = cloneHeader(bodyOpts.Headers) } if bodyOpts.Query != nil { opts.Query = cloneValues(bodyOpts.Query) } if bodyOpts.Alt != "" { opts.Alt = bodyOpts.Alt } if bodyOpts.ImplicitClose != nil { opts.ImplicitClose = *bodyOpts.ImplicitClose } return nil} func applyQueryOptions(opts *runOptions, query url.Values) error { if query == nil { return nil } if raw := strings.TrimSpace(query.Get("model")); raw != "" { opts.Model = raw } if raw := strings.TrimSpace(query.Get("mode")); raw != "" { applyMode(opts, raw) } if raw := strings.TrimSpace(query.Get("entry_protocol")); raw != "" { opts.EntryProtocol = raw } if raw := strings.TrimSpace(query.Get("exit_protocol")); raw != "" { opts.ExitProtocol = raw } if raw := query.Get("prompt"); raw != "" { opts.Prompt = raw } if raw := strings.TrimSpace(query.Get("body")); raw != "" { body := []byte(raw) if !json.Valid(body) { return fmt.Errorf("query body must be valid JSON") } opts.Body = append([]byte(nil), body...) } if raw := strings.TrimSpace(query.Get("alt")); raw != "" { opts.Alt = raw } if errStream := applyBoolQuery(query, "stream", &opts.Stream); errStream != nil { return errStream } if errImplicitClose := applyBoolQuery(query, "implicit_close", &opts.ImplicitClose); errImplicitClose != nil { return errImplicitClose } return nil} func applyMode(opts *runOptions, mode string) { normalized := strings.ToLower(strings.TrimSpace(mode)) switch normalized { case "stream", "streaming": opts.Stream = true case "non-stream", "non_stream", "nonstream", "sync": opts.Stream = false }} func applyBoolQuery(query url.Values, key string, target *bool) error { raw := strings.TrimSpace(query.Get(key)) if raw == "" { return nil } parsed, errParse := strconv.ParseBool(raw) if errParse != nil { return fmt.Errorf("%s must be a boolean: %w", key, errParse) } *target = parsed return nil} func executeOnce(opts runOptions) (pluginapi.HostModelExecutionResponse, error) { body, errBody := modelRequestBody(opts) if errBody != nil { return pluginapi.HostModelExecutionResponse{}, errBody } // Forward HostCallbackID so the host skips this plugin's interceptors on the // nested model execution. Host model callbacks do not recursively call the // originating plugin's interceptor chain. result, errCall := callHost(pluginabi.MethodHostModelExecute, hostModelExecutionRequest{ HostModelExecutionRequest: pluginapi.HostModelExecutionRequest{ EntryProtocol: opts.EntryProtocol, ExitProtocol: opts.ExitProtocol, Model: opts.Model, Stream: false, Body: body, Headers: cloneHeader(opts.Headers), Query: cloneValues(opts.Query), Alt: opts.Alt, }, HostCallbackID: opts.HostCallbackID, }) if errCall != nil { return pluginapi.HostModelExecutionResponse{}, errCall } var resp pluginapi.HostModelExecutionResponse if errUnmarshal := json.Unmarshal(result, &resp); errUnmarshal != nil { return pluginapi.HostModelExecutionResponse{}, fmt.Errorf("decode host.model.execute result: %w", errUnmarshal) } return resp, nil} func executeStream(opts runOptions) (data streamPageData) { body, errBody := modelRequestBody(opts) if errBody != nil { data.Error = errBody.Error() return data } // Forward HostCallbackID so the host skips this plugin's interceptors on the // nested model execution. Host model callbacks do not recursively call the // originating plugin's interceptor chain. result, errCall := callHost(pluginabi.MethodHostModelExecuteStream, hostModelExecutionRequest{ HostModelExecutionRequest: pluginapi.HostModelExecutionRequest{ EntryProtocol: opts.EntryProtocol, ExitProtocol: opts.ExitProtocol, Model: opts.Model, Stream: true, Body: body, Headers: cloneHeader(opts.Headers), Query: cloneValues(opts.Query), Alt: opts.Alt, }, HostCallbackID: opts.HostCallbackID, }) if errCall != nil { data.Error = errCall.Error() return data } var resp pluginapi.HostModelStreamResponse if errUnmarshal := json.Unmarshal(result, &resp); errUnmarshal != nil { data.Error = fmt.Sprintf("decode host.model.execute_stream result: %v", errUnmarshal) return data } data.StatusCode = resp.StatusCode data.Headers = cloneHeader(resp.Headers) data.StreamID = resp.StreamID if resp.StreamID == "" { data.Error = "host.model.execute_stream returned an empty stream_id" return data } if opts.ImplicitClose { // When implicit_close=true, the host closes this stream when the management.handle RPC callback scope returns. data.CloseMode = "implicit close at management.handle return" } else { data.CloseMode = "explicit close through host.model.stream_close" defer func() { if errClose := closeHostModelStream(resp.StreamID); errClose != nil {Lines 1–500 of 731. Load more
package main /* #include <stdint.h> #include <stdlib.h> typedef struct { void* ptr; size_t len; } cliproxy_buffer; typedef int (*cliproxy_host_call_fn)(void*, const char*, const uint8_t*, size_t, cliproxy_buffer*); typedef void (*cliproxy_host_free_fn)(void*, size_t); typedef struct { uint32_t abi_version; void* host_ctx; cliproxy_host_call_fn call; cliproxy_host_free_fn free_buffer; } cliproxy_host_api; typedef int (*cliproxy_plugin_call_fn)(char*, uint8_t*, size_t, cliproxy_buffer*); typedef void (*cliproxy_plugin_free_fn)(void*, size_t); typedef void (*cliproxy_plugin_shutdown_fn)(void); typedef struct { uint32_t abi_version; cliproxy_plugin_call_fn call; cliproxy_plugin_free_fn free_buffer; cliproxy_plugin_shutdown_fn shutdown; } cliproxy_plugin_api; extern int cliproxyPluginCall(char*, uint8_t*, size_t, cliproxy_buffer*); extern void cliproxyPluginFree(void*, size_t); extern void cliproxyPluginShutdown(void); static const cliproxy_host_api* stored_host; static void store_host_api(const cliproxy_host_api* host) { stored_host = host; } static int call_host_api(const char* method, const uint8_t* request, size_t request_len, cliproxy_buffer* response) { if (stored_host == NULL || stored_host->call == NULL) { return 1; } return stored_host->call(stored_host->host_ctx, method, request, request_len, response); } static void free_host_buffer(void* ptr, size_t len) { if (stored_host != NULL && stored_host->free_buffer != NULL && ptr != NULL) { stored_host->free_buffer(ptr, len); } } */ import "C" import ( "bytes" "encoding/json" "fmt" "html" "net/http" "net/url" "strconv" "strings" "unsafe" "github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginabi" "github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginapi" ) const ( defaultModel = "gpt-5.5" defaultPrompt = "Summarize host model callbacks in one short sentence." pluginName = "host-model-callback" resourcePath = "/status" resourceContentType = "text/html; charset=utf-8" ) type envelope struct { OK bool `json:"ok"` Result json.RawMessage `json:"result,omitempty"` Error *envelopeError `json:"error,omitempty"` } type envelopeError struct { Code string `json:"code"` Message string `json:"message"` } type registration struct { SchemaVersion uint32 `json:"schema_version"` Metadata pluginapi.Metadata `json:"metadata"` Capabilities registrationCapabilities `json:"capabilities"` } type registrationCapabilities struct { ManagementAPI bool `json:"management_api"` } type managementRegistration struct { Resources []managementResource `json:"resources,omitempty"` } type managementResource struct { Path string `json:"Path"` Menu string `json:"Menu"` Description string `json:"Description"` } type managementRequest struct { Method string Path string Headers http.Header Query url.Values Body []byte HostCallbackID string `json:"host_callback_id,omitempty"` } type managementResponse struct { StatusCode int `json:"StatusCode"` Headers http.Header `json:"Headers"` Body []byte `json:"Body"` } type managementBodyOptions struct { Model string `json:"model"` Mode string `json:"mode"` EntryProtocol string `json:"entry_protocol"` ExitProtocol string `json:"exit_protocol"` Prompt string `json:"prompt"` Stream *bool `json:"stream"` Body json.RawMessage `json:"body"` Headers http.Header `json:"headers"` Query url.Values `json:"query"` Alt string `json:"alt"` ImplicitClose *bool `json:"implicit_close"` } type hostModelExecutionRequest struct { pluginapi.HostModelExecutionRequest HostCallbackID string `json:"host_callback_id,omitempty"` } type runOptions struct { Model string Mode string EntryProtocol string ExitProtocol string Prompt string Stream bool Body []byte Headers http.Header Query url.Values Alt string ImplicitClose bool HostCallbackID string } type chatCompletionRequest struct { Model string `json:"model"` Stream bool `json:"stream"` Messages []chatMessage `json:"messages"` } type chatMessage struct { Role string `json:"role"` Content string `json:"content"` } type streamPageData struct { StatusCode int Headers http.Header StreamID string Chunks []string Error string CloseMode string CloseError string } func main() {} //export cliproxy_plugin_init func cliproxy_plugin_init(host *C.cliproxy_host_api, plugin *C.cliproxy_plugin_api) C.int { if plugin == nil { return 1 } C.store_host_api(host) plugin.abi_version = C.uint32_t(pluginabi.ABIVersion) plugin.call = C.cliproxy_plugin_call_fn(C.cliproxyPluginCall) plugin.free_buffer = C.cliproxy_plugin_free_fn(C.cliproxyPluginFree) plugin.shutdown = C.cliproxy_plugin_shutdown_fn(C.cliproxyPluginShutdown) return 0 } //export cliproxyPluginCall func cliproxyPluginCall(method *C.char, request *C.uint8_t, requestLen C.size_t, response *C.cliproxy_buffer) C.int { if response != nil { response.ptr = nil response.len = 0 } if method == nil { writeResponse(response, errorEnvelope("invalid_method", "method is required")) return 1 } var requestBytes []byte if request != nil && requestLen > 0 { requestBytes = C.GoBytes(unsafe.Pointer(request), C.int(requestLen)) } raw, errHandle := handleMethod(C.GoString(method), requestBytes) if errHandle != nil { writeResponse(response, errorEnvelope("plugin_error", errHandle.Error())) return 1 } writeResponse(response, raw) return 0 } //export cliproxyPluginFree func cliproxyPluginFree(ptr unsafe.Pointer, len C.size_t) { if ptr != nil { C.free(ptr) } _ = len } //export cliproxyPluginShutdown func cliproxyPluginShutdown() {} func handleMethod(method string, request []byte) ([]byte, error) { switch method { case pluginabi.MethodPluginRegister, pluginabi.MethodPluginReconfigure: return okEnvelope(pluginRegistration()) case pluginabi.MethodManagementRegister: return okEnvelope(managementRegistration{ Resources: []managementResource{{ Path: resourcePath, Menu: "Host Model Callback", Description: "Runs a model request through host.model callbacks and displays the result.", }}, }) case pluginabi.MethodManagementHandle: return handleManagement(request) default: return errorEnvelope("unknown_method", "unknown method: "+method), nil } } func pluginRegistration() registration { return registration{ SchemaVersion: pluginabi.SchemaVersion, Metadata: pluginapi.Metadata{ Name: pluginName, Version: "0.1.0", Author: "router-for-me", GitHubRepository: "https://github.com/router-for-me/CLIProxyAPI", Logo: "https://raw.githubusercontent.com/router-for-me/CLIProxyAPI/main/docs/logo.png", ConfigFields: []pluginapi.ConfigField{}, }, Capabilities: registrationCapabilities{ ManagementAPI: true, }, } } func handleManagement(raw []byte) ([]byte, error) { var req managementRequest if len(raw) > 0 { if errUnmarshal := json.Unmarshal(raw, &req); errUnmarshal != nil { return nil, fmt.Errorf("decode management request: %w", errUnmarshal) } } opts, errOptions := optionsFromManagementRequest(req) if errOptions != nil { page := renderPage(opts, 0, nil, nil, nil, errOptions.Error(), "", "") return okEnvelope(htmlResponse(http.StatusBadRequest, page)) } if opts.Stream { data := executeStream(opts) page := renderPage(opts, data.StatusCode, data.Headers, nil, data.Chunks, data.Error, data.CloseMode, data.CloseError) return okEnvelope(htmlResponse(http.StatusOK, page)) } resp, errExecute := executeOnce(opts) if errExecute != nil { page := renderPage(opts, 0, nil, nil, nil, errExecute.Error(), "", "") return okEnvelope(htmlResponse(http.StatusOK, page)) } page := renderPage(opts, resp.StatusCode, resp.Headers, resp.Body, nil, "", "", "") return okEnvelope(htmlResponse(http.StatusOK, page)) } func optionsFromManagementRequest(req managementRequest) (runOptions, error) { opts := runOptions{ Model: defaultModel, Mode: "non-stream", EntryProtocol: "openai", ExitProtocol: "openai", Prompt: defaultPrompt, Headers: http.Header{}, Query: url.Values{}, } opts.HostCallbackID = strings.TrimSpace(req.HostCallbackID) if len(req.Body) > 0 { if errApplyBody := applyBodyOptions(&opts, req.Body); errApplyBody != nil { return opts, errApplyBody } } if errApplyQuery := applyQueryOptions(&opts, req.Query); errApplyQuery != nil { return opts, errApplyQuery } if opts.Stream { opts.Mode = "stream" } else { opts.Mode = "non-stream" } return opts, nil } func applyBodyOptions(opts *runOptions, raw []byte) error { var bodyOpts managementBodyOptions if errUnmarshal := json.Unmarshal(raw, &bodyOpts); errUnmarshal != nil { return fmt.Errorf("decode JSON request body: %w", errUnmarshal) } if strings.TrimSpace(bodyOpts.Model) != "" { opts.Model = strings.TrimSpace(bodyOpts.Model) } if strings.TrimSpace(bodyOpts.Mode) != "" { applyMode(opts, bodyOpts.Mode) } if strings.TrimSpace(bodyOpts.EntryProtocol) != "" { opts.EntryProtocol = strings.TrimSpace(bodyOpts.EntryProtocol) } if strings.TrimSpace(bodyOpts.ExitProtocol) != "" { opts.ExitProtocol = strings.TrimSpace(bodyOpts.ExitProtocol) } if bodyOpts.Prompt != "" { opts.Prompt = bodyOpts.Prompt } if bodyOpts.Stream != nil { opts.Stream = *bodyOpts.Stream } if len(bodyOpts.Body) > 0 && string(bodyOpts.Body) != "null" { if !json.Valid(bodyOpts.Body) { return fmt.Errorf("body must be valid JSON") } opts.Body = append([]byte(nil), bodyOpts.Body...) } if bodyOpts.Headers != nil { opts.Headers = cloneHeader(bodyOpts.Headers) } if bodyOpts.Query != nil { opts.Query = cloneValues(bodyOpts.Query) } if bodyOpts.Alt != "" { opts.Alt = bodyOpts.Alt } if bodyOpts.ImplicitClose != nil { opts.ImplicitClose = *bodyOpts.ImplicitClose } return nil } func applyQueryOptions(opts *runOptions, query url.Values) error { if query == nil { return nil } if raw := strings.TrimSpace(query.Get("model")); raw != "" { opts.Model = raw } if raw := strings.TrimSpace(query.Get("mode")); raw != "" { applyMode(opts, raw) } if raw := strings.TrimSpace(query.Get("entry_protocol")); raw != "" { opts.EntryProtocol = raw } if raw := strings.TrimSpace(query.Get("exit_protocol")); raw != "" { opts.ExitProtocol = raw } if raw := query.Get("prompt"); raw != "" { opts.Prompt = raw } if raw := strings.TrimSpace(query.Get("body")); raw != "" { body := []byte(raw) if !json.Valid(body) { return fmt.Errorf("query body must be valid JSON") } opts.Body = append([]byte(nil), body...) } if raw := strings.TrimSpace(query.Get("alt")); raw != "" { opts.Alt = raw } if errStream := applyBoolQuery(query, "stream", &opts.Stream); errStream != nil { return errStream } if errImplicitClose := applyBoolQuery(query, "implicit_close", &opts.ImplicitClose); errImplicitClose != nil { return errImplicitClose } return nil } func applyMode(opts *runOptions, mode string) { normalized := strings.ToLower(strings.TrimSpace(mode)) switch normalized { case "stream", "streaming": opts.Stream = true case "non-stream", "non_stream", "nonstream", "sync": opts.Stream = false } } func applyBoolQuery(query url.Values, key string, target *bool) error { raw := strings.TrimSpace(query.Get(key)) if raw == "" { return nil } parsed, errParse := strconv.ParseBool(raw) if errParse != nil { return fmt.Errorf("%s must be a boolean: %w", key, errParse) } *target = parsed return nil } func executeOnce(opts runOptions) (pluginapi.HostModelExecutionResponse, error) { body, errBody := modelRequestBody(opts) if errBody != nil { return pluginapi.HostModelExecutionResponse{}, errBody } // Forward HostCallbackID so the host skips this plugin's interceptors on the // nested model execution. Host model callbacks do not recursively call the // originating plugin's interceptor chain. result, errCall := callHost(pluginabi.MethodHostModelExecute, hostModelExecutionRequest{ HostModelExecutionRequest: pluginapi.HostModelExecutionRequest{ EntryProtocol: opts.EntryProtocol, ExitProtocol: opts.ExitProtocol, Model: opts.Model, Stream: false, Body: body, Headers: cloneHeader(opts.Headers), Query: cloneValues(opts.Query), Alt: opts.Alt, }, HostCallbackID: opts.HostCallbackID, }) if errCall != nil { return pluginapi.HostModelExecutionResponse{}, errCall } var resp pluginapi.HostModelExecutionResponse if errUnmarshal := json.Unmarshal(result, &resp); errUnmarshal != nil { return pluginapi.HostModelExecutionResponse{}, fmt.Errorf("decode host.model.execute result: %w", errUnmarshal) } return resp, nil } func executeStream(opts runOptions) (data streamPageData) { body, errBody := modelRequestBody(opts) if errBody != nil { data.Error = errBody.Error() return data } // Forward HostCallbackID so the host skips this plugin's interceptors on the // nested model execution. Host model callbacks do not recursively call the // originating plugin's interceptor chain. result, errCall := callHost(pluginabi.MethodHostModelExecuteStream, hostModelExecutionRequest{ HostModelExecutionRequest: pluginapi.HostModelExecutionRequest{ EntryProtocol: opts.EntryProtocol, ExitProtocol: opts.ExitProtocol, Model: opts.Model, Stream: true, Body: body, Headers: cloneHeader(opts.Headers), Query: cloneValues(opts.Query), Alt: opts.Alt, }, HostCallbackID: opts.HostCallbackID, }) if errCall != nil { data.Error = errCall.Error() return data } var resp pluginapi.HostModelStreamResponse if errUnmarshal := json.Unmarshal(result, &resp); errUnmarshal != nil { data.Error = fmt.Sprintf("decode host.model.execute_stream result: %v", errUnmarshal) return data } data.StatusCode = resp.StatusCode data.Headers = cloneHeader(resp.Headers) data.StreamID = resp.StreamID if resp.StreamID == "" { data.Error = "host.model.execute_stream returned an empty stream_id" return data } if opts.ImplicitClose { // When implicit_close=true, the host closes this stream when the management.handle RPC callback scope returns. data.CloseMode = "implicit close at management.handle return" } else { data.CloseMode = "explicit close through host.model.stream_close" defer func() { if errClose := closeHostModelStream(resp.StreamID); errClose != nil {