Files
osmedeus/internal/template/precompiled.go
T
j3ssie 1403d20a4d feat: add LLM step executor with vision and tool support, event workflow system, and inheritance
- Add LLM executor supporting OpenAI vision, tool calling, embeddings, and structured outputs
- Introduce event emitter/receiver workflows with deduplication and filtering (generate_event functions)
- Add workflow extends/override system enabling inheritance chains and step merge modes
- Update function naming to snake_case across all testdata (fileExists→file_exists, etc.)
- Add comprehensive test fixtures for linter, events, CDN, step dependencies, and extends workflows
2026-01-20 18:23:57 +08:00

114 lines
2.9 KiB
Go

package template
import (
"strings"
"sync"
"github.com/flosch/pongo2/v6"
)
// WorkflowTemplates holds pre-compiled templates for a single workflow
type WorkflowTemplates struct {
// Templates maps field keys to pre-compiled templates
// Key format: "stepName:fieldName" (e.g., "scan:command", "scan:exports:output")
Templates map[string]*pongo2.Template
}
// PrecompiledRegistry stores pre-compiled templates for workflows.
// Templates are compiled once at workflow load time and reused during execution.
type PrecompiledRegistry struct {
mu sync.RWMutex
workflows map[string]*WorkflowTemplates
}
// NewPrecompiledRegistry creates a new pre-compiled template registry
func NewPrecompiledRegistry() *PrecompiledRegistry {
return &PrecompiledRegistry{
workflows: make(map[string]*WorkflowTemplates),
}
}
// PrecompileWorkflow pre-compiles all template strings for a workflow.
// The templates map should use keys like "stepName:fieldName".
func (r *PrecompiledRegistry) PrecompileWorkflow(workflowName string, templates map[string]string) error {
compiled := &WorkflowTemplates{
Templates: make(map[string]*pongo2.Template, len(templates)),
}
for key, tmplStr := range templates {
// Skip if no template variables
if !strings.Contains(tmplStr, "{{") {
continue
}
tpl, err := pongo2.FromString(tmplStr)
if err != nil {
// Log warning but continue - invalid templates will be handled at runtime
continue
}
compiled.Templates[key] = tpl
}
r.mu.Lock()
r.workflows[workflowName] = compiled
r.mu.Unlock()
return nil
}
// GetPrecompiled retrieves a pre-compiled template for a workflow field.
// Returns nil if not found.
func (r *PrecompiledRegistry) GetPrecompiled(workflowName, key string) any {
r.mu.RLock()
wf, ok := r.workflows[workflowName]
r.mu.RUnlock()
if !ok {
return nil
}
// Note: the WorkflowTemplates map doesn't need locking for reads
// as it's never modified after creation
tpl, exists := wf.Templates[key]
if !exists {
return nil
}
return tpl
}
// ClearPrecompiled removes pre-compiled templates for a workflow
func (r *PrecompiledRegistry) ClearPrecompiled(workflowName string) {
r.mu.Lock()
delete(r.workflows, workflowName)
r.mu.Unlock()
}
// ClearAll removes all pre-compiled templates
func (r *PrecompiledRegistry) ClearAll() {
r.mu.Lock()
r.workflows = make(map[string]*WorkflowTemplates)
r.mu.Unlock()
}
// GetWorkflowCount returns the number of workflows with pre-compiled templates
func (r *PrecompiledRegistry) GetWorkflowCount() int {
r.mu.RLock()
defer r.mu.RUnlock()
return len(r.workflows)
}
// GetTemplateCount returns the total number of pre-compiled templates
func (r *PrecompiledRegistry) GetTemplateCount() int {
r.mu.RLock()
defer r.mu.RUnlock()
count := 0
for _, wf := range r.workflows {
count += len(wf.Templates)
}
return count
}
// Verify interface compliance at compile time
var _ Precompiler = (*PrecompiledRegistry)(nil)