Files
geo/server/internal/tenant/app/template_prompt.go
T
root de30497f59 feat: add tenant and user management with migrations, handlers, and tests
- Implemented tenant and user management features including:
  - Tenant creation and management with associated migrations.
  - User creation and management with associated migrations.
  - Tenant membership management with associated migrations.
  - Platform user roles management with associated migrations.
  - Quota management with associated migrations.
  - Article and template management with associated migrations.
- Added HTTP handlers for templates and workspaces.
- Created tests for protected and public routes.
- Introduced a script to check tenant scope in SQL queries.
- Documented task plan for backend completion and frontend foundation.
2026-04-01 00:58:42 +08:00

181 lines
4.0 KiB
Go

package app
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"unicode/utf8"
)
var promptVariablePattern = regexp.MustCompile(`\{\{\s*([a-zA-Z0-9_]+)\s*\}\}`)
func buildGenerationPrompt(templateName string, promptTemplate *string, params map[string]interface{}) string {
basePrompt := strings.TrimSpace(renderPromptTemplate(promptTemplate, params))
if basePrompt == "" {
basePrompt = fmt.Sprintf("Write a complete article in markdown for the template %q.", templateName)
}
var sections []string
sections = append(sections, basePrompt)
contextBlock := buildPromptContext(params)
if contextBlock != "" {
sections = append(sections, "Request context:\n"+contextBlock)
}
sections = append(sections, strings.Join([]string{
"Output requirements:",
"- Return only article markdown, no surrounding explanation.",
"- Use the provided title when available.",
"- Follow the provided outline sections and key points when available.",
"- Keep structure clear with headings, short paragraphs, and concrete details.",
}, "\n"))
return strings.Join(sections, "\n\n")
}
func renderPromptTemplate(promptTemplate *string, params map[string]interface{}) string {
if promptTemplate == nil {
return ""
}
return promptVariablePattern.ReplaceAllStringFunc(*promptTemplate, func(match string) string {
submatches := promptVariablePattern.FindStringSubmatch(match)
if len(submatches) != 2 {
return match
}
value, ok := params[submatches[1]]
if !ok {
return ""
}
return formatPromptValue(value)
})
}
func buildPromptContext(params map[string]interface{}) string {
if len(params) == 0 {
return ""
}
orderedKeys := []string{
"locale",
"title",
"topic",
"product_name",
"subject",
"brand_name",
"brand",
"category",
"count",
"depth",
"outline_sections",
"key_points",
"keywords",
"competitors",
}
used := make(map[string]struct{}, len(params))
lines := make([]string, 0, len(params))
appendLine := func(key string, value interface{}) {
if value == nil {
return
}
formatted := strings.TrimSpace(formatPromptValue(value))
if formatted == "" {
return
}
lines = append(lines, fmt.Sprintf("- %s: %s", key, formatted))
used[key] = struct{}{}
}
for _, key := range orderedKeys {
appendLine(key, params[key])
}
for key, value := range params {
if _, ok := used[key]; ok {
continue
}
appendLine(key, value)
}
return strings.Join(lines, "\n")
}
func formatPromptValue(value interface{}) string {
switch v := value.(type) {
case nil:
return ""
case string:
return strings.TrimSpace(v)
case []string:
return strings.Join(v, ", ")
case []interface{}:
parts := make([]string, 0, len(v))
for _, item := range v {
formatted := strings.TrimSpace(formatPromptValue(item))
if formatted != "" {
parts = append(parts, formatted)
}
}
return strings.Join(parts, ", ")
default:
bytes, err := json.Marshal(v)
if err != nil {
return fmt.Sprint(v)
}
return string(bytes)
}
}
func resolveArticleTitle(params map[string]interface{}, markdown string) string {
if title := strings.TrimSpace(stringValue(params["title"])); title != "" {
return title
}
if topic := strings.TrimSpace(stringValue(params["topic"])); topic != "" {
return topic
}
if product := strings.TrimSpace(stringValue(params["product_name"])); product != "" {
return product + " Review"
}
if subject := strings.TrimSpace(stringValue(params["subject"])); subject != "" {
return subject + " Report"
}
for _, line := range strings.Split(markdown, "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") {
return strings.TrimSpace(strings.TrimLeft(line, "#"))
}
}
return "Generated Article"
}
func estimateWordCount(markdown string) int {
trimmed := strings.TrimSpace(markdown)
if trimmed == "" {
return 0
}
words := len(strings.Fields(trimmed))
if words > 0 {
return words
}
return utf8.RuneCountInString(trimmed)
}
func stringValue(value interface{}) string {
if value == nil {
return ""
}
text, ok := value.(string)
if !ok {
return ""
}
return text
}