| @@ -21,8 +21,8 @@ Die App kann heute: | |||||
| - Composite-Fallback laeuft nur fuer echte Zielfeld-Luecken: bei voller Primaerabdeckung kein Fallback-Aufruf, bei Teilabdeckung nur fuer fehlende `fieldPath`s; Primaer-Sources bleiben bei Merge priorisiert. | - Composite-Fallback laeuft nur fuer echte Zielfeld-Luecken: bei voller Primaerabdeckung kein Fallback-Aufruf, bei Teilabdeckung nur fuer fehlende `fieldPath`s; Primaer-Sources bleiben bei Merge priorisiert. | ||||
| - Suggestion-Workflow getrennt von Feldwerten (Preview), inkl. `Generate all`, `Regenerate all`, `Apply all to empty` sowie per-Feld `Apply`/`Regenerate` im Draft-/Build-UI. | - Suggestion-Workflow getrennt von Feldwerten (Preview), inkl. `Generate all`, `Regenerate all`, `Apply all to empty` sowie per-Feld `Apply`/`Regenerate` im Draft-/Build-UI. | ||||
| - Technische Felddetails (z. B. `fieldPath`, Suggestion-Metadaten, Slot-Preview) sind im UI standardmaessig ausgeblendet und nur per Debug-Toggle sichtbar. | - Technische Felddetails (z. B. `fieldPath`, Suggestion-Metadaten, Slot-Preview) sind im UI standardmaessig ausgeblendet und nur per Debug-Toggle sichtbar. | ||||
| - Strukturierte Debug-Logs fuer Autofill-/LLM-Flow inkl. Provider-Pfad, QC-Fallback, Rule-based-Fallback und Validate-Action (kurze Metadaten, Fehlerzusammenfassung, Dauer, Suggestion-Count). | |||||
| - Vertiefte Provider-Diagnostics im provider-aware LLM-Pfad (insb. OpenAI-kompatibel): request-nahe Metadaten inkl. Provider/Modell/Base-URL, Prompt-/Payload-Snippets, rohe Response-Snippets plus Shape-Hints sowie Extract-/Parse-/Suggestion-Samples; jeweils sinnvoll gekuerzt und ohne API-Key-/Authorization-Leaks. | |||||
| - Strukturierte Autofill-/LLM-Logs mit operativen Zusammenfassungen auf INFO (Provider-Pfad, Fallback-Status, Validate-Action, Dauer, Suggestion-Count). | |||||
| - Vertiefte Provider-/Runtime-Diagnostik (Prompt-/Payload-/Response-/Extract-/Parse-Snippets, Samples, Feld-Transition-Details) bleibt erhalten und ist ueber `LOG_LEVEL=debug` sichtbar; API-Key-/Authorization-Daten bleiben redigiert. | |||||
| - Kleine interne Log-API `GET /api/logs?limit=<n>` fuer aktuelle In-Memory-Logeintraege (Ring-Buffer, neueste zuerst). | - Kleine interne Log-API `GET /api/logs?limit=<n>` fuer aktuelle In-Memory-Logeintraege (Ring-Buffer, neueste zuerst). | ||||
| - Builds aus geprueften Daten starten sowie Job-Status pollen und Editor-URL nachladen. | - Builds aus geprueften Daten starten sowie Job-Status pollen und Editor-URL nachladen. | ||||
| @@ -39,6 +39,7 @@ Wichtig: | |||||
| - `DB_URL=data/qctextbuilder.db` (Default) | - `DB_URL=data/qctextbuilder.db` (Default) | ||||
| - `QC_BASE_URL=https://qc-api.yggdrasil.dev-mono.net/api/v1` | - `QC_BASE_URL=https://qc-api.yggdrasil.dev-mono.net/api/v1` | ||||
| - `QC_TOKEN=<bearer token>` | - `QC_TOKEN=<bearer token>` | ||||
| - optional: `LOG_LEVEL=info|debug|warn|error` (Default `info`; fuer tiefe Autofill-/Provider-Diagnostik `debug`) | |||||
| - optional: `LOG_FILE=logs/qctextbuilder.log` fuer zusaetzliches JSON-Logfile (stdout bleibt aktiv) | - optional: `LOG_FILE=logs/qctextbuilder.log` fuer zusaetzliches JSON-Logfile (stdout bleibt aktiv) | ||||
| 2. Starten: | 2. Starten: | ||||
| - `go run ./cmd/qctextbuilder` | - `go run ./cmd/qctextbuilder` | ||||
| @@ -50,7 +50,7 @@ Aktueller Stand: | |||||
| - Modellauswahl ist provider-aware statisch umgesetzt und so strukturiert, dass spaeter dynamische Model-Listen/Refresh anschliessbar sind. | - Modellauswahl ist provider-aware statisch umgesetzt und so strukturiert, dass spaeter dynamische Model-Listen/Refresh anschliessbar sind. | ||||
| - Technische Felddetails (z. B. Feldpfade/Slots/Suggestion-Metadaten) sind im UI per Debug-Toggle optional einblendbar. | - Technische Felddetails (z. B. Feldpfade/Slots/Suggestion-Metadaten) sind im UI per Debug-Toggle optional einblendbar. | ||||
| - Strukturierte Debug-Logs fuer den Autofill-/LLM-Pfad sind aktiv (provider-aware Request/Parse, QC-Fallback, Rule-based-Fallback, Validate-Action; ohne Prompt-/Secret-Dumps). | - Strukturierte Debug-Logs fuer den Autofill-/LLM-Pfad sind aktiv (provider-aware Request/Parse, QC-Fallback, Rule-based-Fallback, Validate-Action; ohne Prompt-/Secret-Dumps). | ||||
| - Fokus-Debugging im provider-aware Runtime-Pfad wurde vertieft: gekuerzte Request-/Prompt-/Payload-, Raw-Response-, Shape- und Extract-/Parse-Samples fuer schnellere Ursacheanalyse bei Provider-/Output-Shape-Mismatches; API-Key-/Authorization-Daten bleiben redigiert. | |||||
| - Fokus-Debugging im provider-aware Runtime-Pfad wurde vertieft und per Log-Level gestaffelt: operative Zusammenfassungen laufen auf INFO, tiefe Request-/Prompt-/Payload-, Raw-Response-, Shape-, Extract-/Parse-Samples sowie Feld-Transition-Details auf DEBUG; API-Key-/Authorization-Daten bleiben redigiert. | |||||
| - Eine kleine interne Log-API (`GET /api/logs`) stellt aktuelle strukturierte Logeintraege aus einem In-Memory-Ring-Buffer bereit. | - Eine kleine interne Log-API (`GET /api/logs`) stellt aktuelle strukturierte Logeintraege aus einem In-Memory-Ring-Buffer bereit. | ||||
| - Build-Start erfordert bereits einen Template-Manifest-Status `reviewed`/`validated`. | - Build-Start erfordert bereits einen Template-Manifest-Status `reviewed`/`validated`. | ||||
| - Prozessuale Review-Gates (z. B. Freigabe-Policy, Rollen, Pflichtchecks pro Feld) sind noch nicht vollstaendig ausgebaut. | - Prozessuale Review-Gates (z. B. Freigabe-Policy, Rollen, Pflichtchecks pro Feld) sind noch nicht vollstaendig ausgebaut. | ||||
| @@ -36,7 +36,7 @@ type App struct { | |||||
| } | } | ||||
| func New(cfg config.Config) (*App, error) { | func New(cfg config.Config) (*App, error) { | ||||
| logSetup := logging.Setup() | |||||
| logSetup := logging.Setup(cfg.LogLevel) | |||||
| logger := logSetup.Logger | logger := logSetup.Logger | ||||
| slog.SetDefault(logger) | slog.SetDefault(logger) | ||||
| @@ -10,6 +10,7 @@ type Config struct { | |||||
| DBDriver string | DBDriver string | ||||
| DBURL string | DBURL string | ||||
| AppSecret string | AppSecret string | ||||
| LogLevel string | |||||
| QCBaseURL string | QCBaseURL string | ||||
| QCToken string | QCToken string | ||||
| PollIntervalSeconds int | PollIntervalSeconds int | ||||
| @@ -23,6 +24,7 @@ func Load() Config { | |||||
| DBDriver: getenv("DB_DRIVER", "sqlite"), | DBDriver: getenv("DB_DRIVER", "sqlite"), | ||||
| DBURL: getenv("DB_URL", "data/qctextbuilder.db"), | DBURL: getenv("DB_URL", "data/qctextbuilder.db"), | ||||
| AppSecret: os.Getenv("APP_SECRET"), | AppSecret: os.Getenv("APP_SECRET"), | ||||
| LogLevel: getenv("LOG_LEVEL", "info"), | |||||
| QCBaseURL: getenv("QC_BASE_URL", "https://qc-api.yggdrasil.dev-mono.net/api/v1"), | QCBaseURL: getenv("QC_BASE_URL", "https://qc-api.yggdrasil.dev-mono.net/api/v1"), | ||||
| QCToken: os.Getenv("QC_TOKEN"), | QCToken: os.Getenv("QC_TOKEN"), | ||||
| PollIntervalSeconds: getenvInt("POLL_INTERVAL_SECONDS", 5), | PollIntervalSeconds: getenvInt("POLL_INTERVAL_SECONDS", 5), | ||||
| @@ -681,6 +681,19 @@ func (u *UI) AutofillDraft(w http.ResponseWriter, r *http.Request) { | |||||
| default: | default: | ||||
| msg = "unknown autofill action" | msg = "unknown autofill action" | ||||
| } | } | ||||
| u.logger.InfoContext(r.Context(), "autofill state transition", | |||||
| "component", "autofill", | |||||
| "step", "pre_action_summary", | |||||
| "action", action, | |||||
| "state_count", len(suggestionState.ByFieldPath), | |||||
| "state_sources", summarizeSuggestionSources(suggestionState), | |||||
| ) | |||||
| u.logger.DebugContext(r.Context(), "autofill state transition", | |||||
| "component", "autofill", | |||||
| "step", "pre_action_summary", | |||||
| "action", action, | |||||
| "sample_sources", sampleSuggestionSources(suggestionState, 5), | |||||
| ) | |||||
| sourceCounts := summarizeSuggestionSources(suggestionState) | sourceCounts := summarizeSuggestionSources(suggestionState) | ||||
| u.logger.InfoContext(r.Context(), "autofill action", | u.logger.InfoContext(r.Context(), "autofill action", | ||||
| "component", "autofill", | "component", "autofill", | ||||
| @@ -967,9 +980,17 @@ func validateLLMProviderConfig(ctx context.Context, settings domain.AppSettings, | |||||
| "provider", provider, | "provider", provider, | ||||
| "model", model, | "model", model, | ||||
| "base_url", baseURL, | "base_url", baseURL, | ||||
| "response_snippet", trimSnippet(resp, 40), | |||||
| "duration_ms", time.Since(started).Milliseconds(), | "duration_ms", time.Since(started).Milliseconds(), | ||||
| ) | ) | ||||
| logger.DebugContext(ctx, "validate llm provider config", | |||||
| "component", "autofill", | |||||
| "step", "validate_provider", | |||||
| "status", "success", | |||||
| "provider", provider, | |||||
| "model", model, | |||||
| "base_url", baseURL, | |||||
| "response_snippet", trimSnippet(resp, 40), | |||||
| ) | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -1737,6 +1758,29 @@ func summarizeSuggestionSources(state domain.DraftSuggestionState) map[string]in | |||||
| return out | return out | ||||
| } | } | ||||
| func sampleSuggestionSources(state domain.DraftSuggestionState, limit int) map[string]string { | |||||
| if limit <= 0 || len(state.ByFieldPath) == 0 { | |||||
| return map[string]string{} | |||||
| } | |||||
| paths := make([]string, 0, len(state.ByFieldPath)) | |||||
| for path := range state.ByFieldPath { | |||||
| paths = append(paths, path) | |||||
| } | |||||
| sort.Strings(paths) | |||||
| if len(paths) > limit { | |||||
| paths = paths[:limit] | |||||
| } | |||||
| out := make(map[string]string, len(paths)) | |||||
| for _, path := range paths { | |||||
| source := strings.TrimSpace(state.ByFieldPath[path].Source) | |||||
| if source == "" { | |||||
| source = "unknown" | |||||
| } | |||||
| out[path] = source | |||||
| } | |||||
| return out | |||||
| } | |||||
| func shortError(err error) string { | func shortError(err error) string { | ||||
| if err == nil { | if err == nil { | ||||
| return "" | return "" | ||||
| @@ -96,7 +96,7 @@ func (c *openAICompatibleClient) Generate(ctx context.Context, req Request) (str | |||||
| payload[maxTokensField] = optionalInt(req.MaxTokens, 1200) | payload[maxTokensField] = optionalInt(req.MaxTokens, 1200) | ||||
| payloadRaw, _ := json.Marshal(payload) | payloadRaw, _ := json.Marshal(payload) | ||||
| payloadSnippet := redactSecrets(snippet(string(payloadRaw), runtimePayloadSnippetLimit), req.APIKey) | payloadSnippet := redactSecrets(snippet(string(payloadRaw), runtimePayloadSnippetLimit), req.APIKey) | ||||
| runtimeLogger().InfoContext(ctx, "llm runtime", | |||||
| runtimeLogger().DebugContext(ctx, "llm runtime", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "provider_http_request", | "step", "provider_http_request", | ||||
| "provider", provider, | "provider", provider, | ||||
| @@ -116,7 +116,7 @@ func (c *openAICompatibleClient) Generate(ctx context.Context, req Request) (str | |||||
| return "", err | return "", err | ||||
| } | } | ||||
| rawResponse := strings.TrimSpace(string(body)) | rawResponse := strings.TrimSpace(string(body)) | ||||
| runtimeLogger().InfoContext(ctx, "llm runtime", | |||||
| runtimeLogger().DebugContext(ctx, "llm runtime", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "provider_http_response", | "step", "provider_http_response", | ||||
| "provider", provider, | "provider", provider, | ||||
| @@ -130,7 +130,7 @@ func (c *openAICompatibleClient) Generate(ctx context.Context, req Request) (str | |||||
| return "", fmt.Errorf("decode openai-compatible response: %w", err) | return "", fmt.Errorf("decode openai-compatible response: %w", err) | ||||
| } | } | ||||
| shape := describeOpenAICompatibleShape(response) | shape := describeOpenAICompatibleShape(response) | ||||
| runtimeLogger().InfoContext(ctx, "llm runtime", | |||||
| runtimeLogger().DebugContext(ctx, "llm runtime", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "provider_http_response_shape", | "step", "provider_http_response_shape", | ||||
| "provider", provider, | "provider", provider, | ||||
| @@ -138,7 +138,7 @@ func (c *openAICompatibleClient) Generate(ctx context.Context, req Request) (str | |||||
| "response_shape_hint", snippet(shape, runtimeShapeSnippetLimit), | "response_shape_hint", snippet(shape, runtimeShapeSnippetLimit), | ||||
| ) | ) | ||||
| content := extractOpenAICompatibleContent(response) | content := extractOpenAICompatibleContent(response) | ||||
| runtimeLogger().InfoContext(ctx, "llm runtime", | |||||
| runtimeLogger().DebugContext(ctx, "llm runtime", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "provider_extract", | "step", "provider_extract", | ||||
| "provider", provider, | "provider", provider, | ||||
| @@ -239,15 +239,17 @@ type SetupResult struct { | |||||
| Close func() error | Close func() error | ||||
| } | } | ||||
| func Setup() SetupResult { | |||||
| func Setup(levelRaw string) SetupResult { | |||||
| level := parseLevel(levelRaw) | |||||
| handlerOptions := &slog.HandlerOptions{Level: level} | |||||
| recent := NewRecentStore(400) | recent := NewRecentStore(400) | ||||
| stdoutHandler := slog.NewJSONHandler(os.Stdout, nil) | |||||
| handlers := []slog.Handler{stdoutHandler, NewRecentHandler(recent, slog.LevelInfo)} | |||||
| stdoutHandler := slog.NewJSONHandler(os.Stdout, handlerOptions) | |||||
| handlers := []slog.Handler{stdoutHandler, NewRecentHandler(recent, level)} | |||||
| closers := make([]io.Closer, 0, 1) | closers := make([]io.Closer, 0, 1) | ||||
| if path := strings.TrimSpace(os.Getenv("LOG_FILE")); path != "" { | if path := strings.TrimSpace(os.Getenv("LOG_FILE")); path != "" { | ||||
| if file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644); err == nil { | if file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644); err == nil { | ||||
| handlers = append(handlers, slog.NewJSONHandler(file, nil)) | |||||
| handlers = append(handlers, slog.NewJSONHandler(file, handlerOptions)) | |||||
| closers = append(closers, file) | closers = append(closers, file) | ||||
| } | } | ||||
| } | } | ||||
| @@ -264,3 +266,18 @@ func Setup() SetupResult { | |||||
| }, | }, | ||||
| } | } | ||||
| } | } | ||||
| func parseLevel(raw string) slog.Level { | |||||
| switch strings.ToLower(strings.TrimSpace(raw)) { | |||||
| case "trace": | |||||
| return slog.LevelDebug - 4 | |||||
| case "debug": | |||||
| return slog.LevelDebug | |||||
| case "warn", "warning": | |||||
| return slog.LevelWarn | |||||
| case "error": | |||||
| return slog.LevelError | |||||
| default: | |||||
| return slog.LevelInfo | |||||
| } | |||||
| } | |||||
| @@ -123,7 +123,7 @@ func (g *ProviderAwareSuggestionGenerator) Generate(ctx context.Context, req Sug | |||||
| systemPrompt, userPrompt := buildProviderPrompts(req, targets) | systemPrompt, userPrompt := buildProviderPrompts(req, targets) | ||||
| temperature := domain.NormalizeLLMTemperature(settings.LLMTemperature) | temperature := domain.NormalizeLLMTemperature(settings.LLMTemperature) | ||||
| maxTokens := domain.NormalizeLLMMaxTokens(settings.LLMMaxTokens) | maxTokens := domain.NormalizeLLMMaxTokens(settings.LLMMaxTokens) | ||||
| mappingLogger().InfoContext(ctx, "provider-aware suggestion", | |||||
| mappingLogger().DebugContext(ctx, "provider-aware suggestion", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "provider_aware_request_payload", | "step", "provider_aware_request_payload", | ||||
| "status", "start", | "status", "start", | ||||
| @@ -170,9 +170,18 @@ func (g *ProviderAwareSuggestionGenerator) Generate(ctx context.Context, req Sug | |||||
| "template_id", req.TemplateID, | "template_id", req.TemplateID, | ||||
| "draft_id", strings.TrimSpace(req.DraftID), | "draft_id", strings.TrimSpace(req.DraftID), | ||||
| "response_chars", len(strings.TrimSpace(raw)), | "response_chars", len(strings.TrimSpace(raw)), | ||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "provider-aware suggestion", | |||||
| "component", "autofill", | |||||
| "step", "provider_aware_request", | |||||
| "status", "success", | |||||
| "provider", provider, | |||||
| "model", model, | |||||
| "template_id", req.TemplateID, | |||||
| "draft_id", strings.TrimSpace(req.DraftID), | |||||
| "response_snippet", providerLogSnippet(raw, 4000), | "response_snippet", providerLogSnippet(raw, 4000), | ||||
| ) | ) | ||||
| mappingLogger().InfoContext(ctx, "provider-aware suggestion", | |||||
| mappingLogger().DebugContext(ctx, "provider-aware suggestion", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "provider_parse_input", | "step", "provider_parse_input", | ||||
| "status", "start", | "status", "start", | ||||
| @@ -208,6 +217,15 @@ func (g *ProviderAwareSuggestionGenerator) Generate(ctx context.Context, req Sug | |||||
| "template_id", req.TemplateID, | "template_id", req.TemplateID, | ||||
| "draft_id", strings.TrimSpace(req.DraftID), | "draft_id", strings.TrimSpace(req.DraftID), | ||||
| "parsed_count", len(parsed), | "parsed_count", len(parsed), | ||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "provider-aware suggestion", | |||||
| "component", "autofill", | |||||
| "step", "provider_parse", | |||||
| "status", "success", | |||||
| "provider", provider, | |||||
| "model", model, | |||||
| "template_id", req.TemplateID, | |||||
| "draft_id", strings.TrimSpace(req.DraftID), | |||||
| "parsed_sample", providerSuggestionSample(parsed, 5), | "parsed_sample", providerSuggestionSample(parsed, 5), | ||||
| ) | ) | ||||
| @@ -247,9 +265,19 @@ func (g *ProviderAwareSuggestionGenerator) Generate(ctx context.Context, req Sug | |||||
| "template_id", req.TemplateID, | "template_id", req.TemplateID, | ||||
| "draft_id", strings.TrimSpace(req.DraftID), | "draft_id", strings.TrimSpace(req.DraftID), | ||||
| "suggestion_count", len(out.Suggestions), | "suggestion_count", len(out.Suggestions), | ||||
| "sources", summarizeResultSources(out), | |||||
| "duration_ms", time.Since(started).Milliseconds(), | |||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "provider-aware suggestion", | |||||
| "component", "autofill", | |||||
| "step", "provider_aware_result", | |||||
| "status", "success", | |||||
| "provider", provider, | |||||
| "model", model, | |||||
| "template_id", req.TemplateID, | |||||
| "draft_id", strings.TrimSpace(req.DraftID), | |||||
| "suggestion_sample_sources", sampleResultSources(out, 5), | "suggestion_sample_sources", sampleResultSources(out, 5), | ||||
| "suggestion_sample", suggestionLogSample(out, 5), | "suggestion_sample", suggestionLogSample(out, 5), | ||||
| "duration_ms", time.Since(started).Milliseconds(), | |||||
| ) | ) | ||||
| return out, nil | return out, nil | ||||
| } | } | ||||
| @@ -311,6 +311,11 @@ func GenerateAllSuggestions(ctx context.Context, generator SuggestionGenerator, | |||||
| "action", "generate_all", | "action", "generate_all", | ||||
| "generated_count", len(generated.ByFieldPath), | "generated_count", len(generated.ByFieldPath), | ||||
| "generated_sources", summarizeResultSources(generated), | "generated_sources", summarizeResultSources(generated), | ||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "autofill state transition", | |||||
| "component", "autofill", | |||||
| "step", "post_generate_result", | |||||
| "action", "generate_all", | |||||
| "sample_sources", sampleResultSources(generated, 5), | "sample_sources", sampleResultSources(generated, 5), | ||||
| ) | ) | ||||
| for _, s := range generated.Suggestions { | for _, s := range generated.Suggestions { | ||||
| @@ -329,7 +334,7 @@ func GenerateAllSuggestions(ctx context.Context, generator SuggestionGenerator, | |||||
| if explicitSource := strings.TrimSpace(s.Source); explicitSource != "" { | if explicitSource := strings.TrimSpace(s.Source); explicitSource != "" { | ||||
| stored.Source = explicitSource | stored.Source = explicitSource | ||||
| } | } | ||||
| mappingLogger().InfoContext(ctx, "autofill state transition", | |||||
| mappingLogger().DebugContext(ctx, "autofill state transition", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "apply_field_transition", | "step", "apply_field_transition", | ||||
| "action", "generate_all", | "action", "generate_all", | ||||
| @@ -346,6 +351,11 @@ func GenerateAllSuggestions(ctx context.Context, generator SuggestionGenerator, | |||||
| "action", "generate_all", | "action", "generate_all", | ||||
| "state_count", len(next.ByFieldPath), | "state_count", len(next.ByFieldPath), | ||||
| "state_sources", summarizeDraftSuggestionSources(next), | "state_sources", summarizeDraftSuggestionSources(next), | ||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "autofill state transition", | |||||
| "component", "autofill", | |||||
| "step", "post_generate_apply_state", | |||||
| "action", "generate_all", | |||||
| "sample_sources", sampleDraftSuggestionSources(next, 5), | "sample_sources", sampleDraftSuggestionSources(next, 5), | ||||
| ) | ) | ||||
| next.UpdatedAt = now.UTC() | next.UpdatedAt = now.UTC() | ||||
| @@ -375,6 +385,11 @@ func RegenerateAllSuggestions(ctx context.Context, generator SuggestionGenerator | |||||
| "action", "regenerate_all", | "action", "regenerate_all", | ||||
| "generated_count", len(generated.ByFieldPath), | "generated_count", len(generated.ByFieldPath), | ||||
| "generated_sources", summarizeResultSources(generated), | "generated_sources", summarizeResultSources(generated), | ||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "autofill state transition", | |||||
| "component", "autofill", | |||||
| "step", "post_generate_result", | |||||
| "action", "regenerate_all", | |||||
| "sample_sources", sampleResultSources(generated, 5), | "sample_sources", sampleResultSources(generated, 5), | ||||
| ) | ) | ||||
| for _, s := range generated.Suggestions { | for _, s := range generated.Suggestions { | ||||
| @@ -388,7 +403,7 @@ func RegenerateAllSuggestions(ctx context.Context, generator SuggestionGenerator | |||||
| if explicitSource := strings.TrimSpace(s.Source); explicitSource != "" { | if explicitSource := strings.TrimSpace(s.Source); explicitSource != "" { | ||||
| stored.Source = explicitSource | stored.Source = explicitSource | ||||
| } | } | ||||
| mappingLogger().InfoContext(ctx, "autofill state transition", | |||||
| mappingLogger().DebugContext(ctx, "autofill state transition", | |||||
| "component", "autofill", | "component", "autofill", | ||||
| "step", "apply_field_transition", | "step", "apply_field_transition", | ||||
| "action", "regenerate_all", | "action", "regenerate_all", | ||||
| @@ -405,6 +420,11 @@ func RegenerateAllSuggestions(ctx context.Context, generator SuggestionGenerator | |||||
| "action", "regenerate_all", | "action", "regenerate_all", | ||||
| "state_count", len(next.ByFieldPath), | "state_count", len(next.ByFieldPath), | ||||
| "state_sources", summarizeDraftSuggestionSources(next), | "state_sources", summarizeDraftSuggestionSources(next), | ||||
| ) | |||||
| mappingLogger().DebugContext(ctx, "autofill state transition", | |||||
| "component", "autofill", | |||||
| "step", "post_generate_apply_state", | |||||
| "action", "regenerate_all", | |||||
| "sample_sources", sampleDraftSuggestionSources(next, 5), | "sample_sources", sampleDraftSuggestionSources(next, 5), | ||||
| ) | ) | ||||
| next.UpdatedAt = now.UTC() | next.UpdatedAt = now.UTC() | ||||