api/transport.go
Snider fb498f0b88 feat(api): canonical webhook events + chat completions transport discovery
Implements gaps between RFC.md spec and code:

- Export canonical webhook event identifiers (RFC §6) as Go constants:
  WebhookEventWorkspaceCreated, WebhookEventLinkClicked, etc. Plus
  WebhookEvents() and IsKnownWebhookEvent(name) helpers for SDK consumers
  and middleware validation.

- Surface the chat completions endpoint (RFC §11.1) through TransportConfig
  (ChatCompletionsEnabled + ChatCompletionsPath) and the OpenAPI spec
  extensions (x-chat-completions-enabled, x-chat-completions-path) so
  clients can auto-discover the local OpenAI-compatible endpoint.

- Add internal test coverage for chat completions sampling defaults
  (Gemma 4 calibrated temp=1.0, top_p=0.95, top_k=64, max_tokens=2048)
  and the ThinkingExtractor channel routing (RFC §11.6).

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 15:02:18 +01:00

88 lines
2.6 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package api
import core "dappco.re/go/core"
// TransportConfig captures the configured transport endpoints and flags for an Engine.
//
// It is intentionally small and serialisable so callers can inspect the active HTTP
// surface without rebuilding an OpenAPI document.
//
// Example:
//
// cfg := api.TransportConfig{SwaggerPath: "/swagger", WSPath: "/ws"}
type TransportConfig struct {
SwaggerEnabled bool
SwaggerPath string
GraphQLPath string
GraphQLEnabled bool
GraphQLPlayground bool
GraphQLPlaygroundPath string
WSEnabled bool
WSPath string
SSEEnabled bool
SSEPath string
PprofEnabled bool
ExpvarEnabled bool
ChatCompletionsEnabled bool
ChatCompletionsPath string
}
// TransportConfig returns the currently configured transport metadata for the engine.
//
// The result snapshots the Engine state at call time and normalises any configured
// URL paths using the same rules as the runtime handlers.
//
// Example:
//
// cfg := engine.TransportConfig()
func (e *Engine) TransportConfig() TransportConfig {
if e == nil {
return TransportConfig{}
}
cfg := TransportConfig{
SwaggerEnabled: e.swaggerEnabled,
WSEnabled: e.wsHandler != nil || e.wsGinHandler != nil,
SSEEnabled: e.sseBroker != nil,
PprofEnabled: e.pprofEnabled,
ExpvarEnabled: e.expvarEnabled,
ChatCompletionsEnabled: e.chatCompletionsResolver != nil,
}
gql := e.GraphQLConfig()
cfg.GraphQLEnabled = gql.Enabled
cfg.GraphQLPlayground = gql.Playground
cfg.GraphQLPlaygroundPath = gql.PlaygroundPath
if e.swaggerEnabled || core.Trim(e.swaggerPath) != "" {
cfg.SwaggerPath = resolveSwaggerPath(e.swaggerPath)
}
if gql.Path != "" {
cfg.GraphQLPath = gql.Path
}
if e.wsHandler != nil || e.wsGinHandler != nil || core.Trim(e.wsPath) != "" {
cfg.WSPath = resolveWSPath(e.wsPath)
}
if e.sseBroker != nil || core.Trim(e.ssePath) != "" {
cfg.SSEPath = resolveSSEPath(e.ssePath)
}
if e.chatCompletionsResolver != nil || core.Trim(e.chatCompletionsPath) != "" {
cfg.ChatCompletionsPath = resolveChatCompletionsPath(e.chatCompletionsPath)
}
return cfg
}
// resolveChatCompletionsPath returns the configured chat completions path or
// the spec §11.1 default when no override has been provided.
func resolveChatCompletionsPath(path string) string {
path = core.Trim(path)
if path == "" {
return defaultChatCompletionsPath
}
if !core.HasPrefix(path, "/") {
path = "/" + path
}
return path
}