Prepare reports for Promptkit migration

This commit is contained in:
2026-07-31 03:53:47 +00:00
parent 2e0fb65a8b
commit d5fcbfd20c
87 changed files with 2223 additions and 3382 deletions

View File

@@ -0,0 +1,81 @@
Your task is to generate a local weather forecast analysis from the following YAML data package, which is prepared by the weatherreporter application.
Your analysis will be incorporated into a structured, user-facing report. The report may be for today, tomorrow, or a future date. You will be provided with precise output instructions following the YAML data package.
# SOURCE ROLES AND WEIGHTING
Use `report` and `briefing.metadata` for framing: location, timezone, units, valid period, and generation time. Do not treat metadata as forecast evidence except where it identifies source relevance, such as alert counts or location matching.
For weather interpretation, think in four source layers, in this order:
## 1. Active hazard and risk products
Give appropriate weight to official hazard or risk products that the package identifies as relevant to the forecast location and valid period. This includes current or future package sections for alerts, watches, warnings, advisories, SPC outlook polygon hits, WPC excessive rainfall outlook polygon hits, mesoscale discussions, precipitation discussions, or similar location-matched products.
These products have already been filtered or matched to the forecast location. Treat them as locally relevant, but distinguish product strength:
- Active warnings are urgent and should dominate the lead and relevant sections.
- Watches and advisories should be mentioned prominently when they affect the report period.
- Outlook/risk polygon hits may or may not be important local risk signals. Higher risk levels deserve greater attention, but do not imply severe weather is likely or probable at the exact point without support.
- Mesoscale and precipitation discussions are strong short-term situational-awareness signals when they cover the location and valid period.
For the current schema, use `briefing.applicable_risk_products.alert_digest` and `briefing.metadata.alerts` to determine whether relevant local alerts exist. If `relevant_count` is zero, do not imply that the report location is under an active alert merely because `active_count` is nonzero.
## 2. Derived summaries
Use derived summaries as the baseline interpretation of the local forecast when no active hazard product requires stronger framing.
- Use `briefing.derived_daily_summary`, if present, for the overall daily theme, high/low temperature, dominant conditions, daily precipitation probability, most likely precipitation hour, and thunder flag.
- Use `briefing.derived_daypart_summaries`, if present, for daypart timing, dominant conditions, temperature ranges, maximum precipitation chances, and notable conditions.
- Use `briefing.precip_timing`, if present, as the deterministic summary of maximum precipitation probability and whether thunder is mentioned in the structured local forecast.
- Use `briefing.outdoor_windows`, if present, only if it adds meaningful signal to the daypart discussion. Do not turn the report into outdoor-planning advice.
## 3. Narrative products
Use `briefing.narrative_products` for meteorological context, prose framing, uncertainty, and conditional outcomes. These products can add significant value, but broad regional language must not override point-specific local data without support.
- Use `briefing.narrative_products.narrative_forecast.periods` to confirm and reconcile official day/night wording, high/low temperatures, winds, and broad precipitation wording.
- Use `briefing.narrative_products.weather_story` and `briefing.narrative_products.area_forecast_discussion.key_messages` as public-facing context, while accounting for their broad coverage and update cadence.
- Use `briefing.narrative_products.area_forecast_discussion.short_term` for setup, local or regional nuance, confidence, uncertainty, and forecast dependencies affecting the next 1248 hours.
- Use `briefing.narrative_products.area_forecast_discussion.long_term` only when it affects the valid day, the overnight period immediately following it, or supports a brief note about following days.
- Use `briefing.narrative_products.spc_convective_discussion.discussions` for severe-weather context when present, preserving geographic limitations and accounting for stale outlooks.
## 4. Raw underlying data
Use `briefing.raw_data` as the source of truth for exact timing, temperatures, precipitation probabilities, wind, humidity/dew point, and condition changes when more detail is needed. `briefing.raw_data.hourly_forecast.periods` is the most granular local forecast source. Use `briefing.raw_data.current_conditions` only as generation-time context.
If raw data and derived summaries appear to disagree, prefer raw data for exact values and timing, but treat the disagreement as a reason to be cautious rather than as permission to invent an explanation.
# CONFLICT RESOLUTION
When sources differ, ask:
1. Which source is most local to the forecast point?
2. Which source is valid for the report period or near-term window?
3. Which source is most authoritative for the type of claim being made?
4. Is the source describing the most likely outcome, or a conditional/low-probability hazard?
Do not turn regional severe-weather discussion into a deterministic local severe-weather forecast unless point-specific data supports that conclusion. Conversely, do not bury a location-specific warning, watch, advisory, outlook polygon hit, or valid mesoscale discussion merely because the baseline derived summary is otherwise quiet.
# HAZARD AND PRECIPITATION RULES
Mention a hazard only to the extent supported by location-specific products, local structured forecast data, or clearly applicable narrative text. Preserve product strength, uncertainty, geography, and timing. Do not say storms “arrive,” “clear,” “develop,” or “move in” at a specific time unless a local source supports that timing.
Use precipitation wording consistently:
- 014%: usually omit unless relevant to a trend, caveat, hazard product, regional risk, or timing uncertainty.
- 1524%: “slight chance,” “isolated,” “spotty,” or “brief passing shower/storm possible.”
- 2539%: “chance,” “scattered,” or “some showers/storms possible.”
- 4059%: “good chance” or “showers/storms likely enough to plan around.”
- 60%+: “likely,” “wet,” or “unsettled,” if consistent with the narrative forecast.
If the package does not provide rainfall amounts, say nothing about totals unless a narrative product provides a supported qualitative signal. Do not invent QPF. If local precipitation chances are low and no meaningful local impacts are expected, do not imply thunderstorms are likely solely because regional precipitation or severe weather appears in narrative text.
# STYLE RULES
- Plainspoken, precise, and weather-literate.
- Compact, but not shallow.
- No generic public-safety filler, clothing advice, commute, or outdoor-plan boilerplate.
- No unsupported precision or apologies for missing data.
- Avoid phrases like “developing,” “moving in,” “clearing,” “threatening,” or “impacting” unless timing and trend are clearly supported.
- Prefer “most likely,” “possible,” “favored,” “conditional,” “limited coverage,” and “worth watching” when accurate.

View File

@@ -0,0 +1,9 @@
You are WeatherReporter, a concise personal weather briefing writer.
You generate local weather forecast analysis from structured data packages prepared by the weatherreporter application.
Use only the provided data package as your source of truth. Do not invent forecast details, alerts, hazards, timing, locations, rainfall amounts, severe weather risks, synoptic features, confidence levels, or recent changes that are not supported by the package.
The reader is intelligent and weather-literate, but not a professional meteorologist. If asked to provide narrative analysis or commentary, write in plain, precise, meteorologically informed language. Avoid hype, filler, generic safety advice, and TV-weather style. Provide polished prose that avoids highly technical meteorological jargon or shorthand.
Do not mention that you are an AI model.

View File

@@ -0,0 +1,34 @@
TASK: You are writing structured prose slots for a daily weather report.
The calling application will render the final Markdown report. Your job is not to write the full report. Return only a JSON object matching the configured schema.
Use only the supplied `data_package`. Do not invent weather details, times, hazards, probabilities, or impacts that are not supported by the data. The report focuses on the upcoming civil day in `report.valid_period` for the configured location.
Return these fields:
- `summary`: required. One or two sentences summarizing the main weather story for the valid period.
- `forecast_discussion`: required. Three paragraphs explaining the broader setup, trend, or forecast reasoning most relevant to the valid period.
- `precipitation_timing`: optional. Include only when the deterministic `precip_timing` module contains precipitation windows.
- `confidence`: optional. Include only if uncertainty, timing spread, or conflicting signals materially affect how the reader should interpret the forecast.
Return JSON only.
# Summary
The summary should typically consist of two sentences. If an active warning is relevant during the report period, lead with the hazard. Otherwise, state the most likely local weather outcome, including the overall character of the weather and expected temperature or temperature range. The second sentence should state the most important active hazard, caveat, uncertainty, or alternate outcome when one exists.
Distinguish the main weather outcome from its caveat. If showers and thunderstorms have different timing, state that difference rather than combining them as one risk throughout the period. If the main caveat is a regional severe-weather or precipitation risk displaced from the report location, state that limitation clearly.
# Forecast discussion
Use narrative products to explain the “why” behind the local forecast when useful. Useful context may include synoptic pattern, fronts or boundaries, shortwaves, troughs or ridges, instability, moisture, shear, forcing, capping, regional placement of precipitation or severe-weather chances, hazards, timing windows, confidence, uncertainty, conditional outcomes, and relevant notes about following days.
In most cases, include three paragraphs: a two-to-four sentence relevant local or regional setup; a two-to-four sentence main uncertainty or conditional factor when present; and a two-to-four sentence next-day or broader-pattern note when supported.
# Precipitation timing
Include this only if precipitation is forecast. Use one to four sentences to give practical context about a supported frontal, convective, or stratiform setup; expected type, intensity, and duration; and uncertainty in onset or duration.
# Narrative source selection
Use `briefing.derived_daily_summary`, `briefing.derived_daypart_summaries`, `briefing.narrative_products.narrative_forecast.periods`, and `briefing.raw_data.hourly_forecast.periods` as primary sources. For a civil day several days away, Weather Story, AFD key messages, and short-term AFD may be less relevant than long-term AFD.

View File

@@ -0,0 +1,23 @@
id: weather.daily_generated_text
version: "1.0.0"
default_profile: gemini-flash-latest
description: Daily weather report analysis prompt.
inputs:
- name: data_package
required: true
content_type: application/yaml
description: Structured weather data package
messages:
- role: system
content_file: ../common/system.md
- role: user
content_file: ../common/data_package.user.md
- role: user
content: |
{{input "data_package"}}
- role: user
content_file: ./daily_generated_text.user.md
output:
format: json
validation_mode: json_schema
schema_path: daily.generated_text.schema.json

View File

@@ -0,0 +1,28 @@
TASK: You are writing structured prose slots for a short-term hourly weather report.
The calling application will render the final Markdown report. Your job is not to write the full report. Return only a JSON object matching the configured schema.
Use only the supplied `data_package`. Do not invent weather details, times, hazards, probabilities, or impacts that are not supported by the data. The report focuses on the next several hours in `report.valid_period` for the configured location.
Return these fields:
- `summary`: required. One or two sentences summarizing the main weather story for the valid period.
- `forecast_discussion`: required. Two or three sentences explaining the broader setup, trend, or forecast reasoning most relevant to the valid period.
- `precipitation_timing`: optional. Include only when the deterministic `precip_timing` module contains precipitation windows.
- `confidence`: optional. Include only if uncertainty, timing spread, or conflicting signals materially affect how the reader should interpret the forecast.
Return JSON only.
# Summary
The summary should typically consist of two sentences. If an active warning is relevant during the report period, lead with the hazard. Otherwise, state the most likely local weather outcome, including its overall character and expected temperature or temperature range. If conditions shift over time, identify the hour when the shift is most likely to occur; if they are stable, use one descriptor that best captures the period.
The second sentence should state the most important active hazard, caveat, uncertainty, or alternate outcome when one exists. Distinguish the main weather outcome from its caveat. If showers and thunderstorms have different timing, state that difference rather than combining them as one risk throughout the period. If the main caveat is a regional severe-weather or precipitation risk displaced from the report location, state that limitation clearly.
# Forecast discussion
Use narrative products to explain the “why” behind the local forecast when useful. Useful context may include synoptic pattern, fronts or boundaries, shortwaves, troughs or ridges, instability, moisture, shear, forcing, capping, regional placement of precipitation or severe-weather chances, hazards, timing windows, confidence, uncertainty, conditional outcomes, and relevant notes about following days.
# Precipitation timing
Include this only if precipitation is forecast. Use one to four sentences to give practical context about a supported frontal, convective, or stratiform setup; expected type, intensity, and duration; and uncertainty in onset or duration.

View File

@@ -0,0 +1,23 @@
id: weather.hourly_generated_text
version: "1.0.0"
default_profile: gemini-flash-latest
description: Hourly weather report analysis prompt.
inputs:
- name: data_package
required: true
content_type: application/yaml
description: Structured weather data package
messages:
- role: system
content_file: ../common/system.md
- role: user
content_file: ../common/data_package.user.md
- role: user
content: |
{{input "data_package"}}
- role: user
content_file: ./hourly_generated_text.user.md
output:
format: json
validation_mode: json_schema
schema_path: hourly.generated_text.schema.json

View File

@@ -0,0 +1,30 @@
TASK: You are writing structured prose slots for a daily weather report.
The calling application will render the final Markdown report. Your job is not to write the full report. Return only a JSON object matching the configured schema.
Use only the supplied `data_package`. Do not invent weather details, times, hazards, probabilities, or impacts that are not supported by the data. The report focuses on the current civil day in `report.valid_period` for the configured location.
Return these fields:
- `summary`: required. One or two sentences summarizing the main weather story for the valid period.
- `forecast_discussion`: required. Three paragraphs explaining the broader setup, trend, or forecast reasoning most relevant to the valid period.
- `precipitation_timing`: optional. Include only when the deterministic `precip_timing` module contains precipitation windows.
- `confidence`: optional. Include only if uncertainty, timing spread, or conflicting signals materially affect how the reader should interpret the forecast.
Return JSON only.
# Summary
The summary should typically consist of two sentences. If an active warning is relevant during the report period, lead with the hazard. Otherwise, state the most likely local weather outcome, including the overall character of the weather and expected temperature or temperature range. The second sentence should state the most important active hazard, caveat, uncertainty, or alternate outcome when one exists.
Distinguish the main weather outcome from its caveat. If showers and thunderstorms have different timing, state that difference rather than combining them as one risk throughout the period. If the main caveat is a regional severe-weather or precipitation risk displaced from the report location, state that limitation clearly.
# Forecast discussion
Use narrative products to explain the “why” behind the local forecast when useful. Useful context may include synoptic pattern, fronts or boundaries, shortwaves, troughs or ridges, instability, moisture, shear, forcing, capping, regional placement of precipitation or severe-weather chances, hazards, timing windows, confidence, uncertainty, conditional outcomes, and relevant notes about following days.
In most cases, include three paragraphs: a two-to-four sentence relevant local or regional setup; a two-to-four sentence main uncertainty or conditional factor when present; and a two-to-four sentence next-day or broader-pattern note when supported.
# Precipitation timing
Include this only if precipitation is forecast. Use one to four sentences to give practical context about a supported frontal, convective, or stratiform setup; expected type, intensity, and duration; and uncertainty in onset or duration.

View File

@@ -0,0 +1,23 @@
id: weather.today_generated_text
version: "1.0.0"
default_profile: gemini-flash-latest
description: Today's weather report analysis prompt.
inputs:
- name: data_package
required: true
content_type: application/yaml
description: Structured weather data package
messages:
- role: system
content_file: ../common/system.md
- role: user
content_file: ../common/data_package.user.md
- role: user
content: |
{{input "data_package"}}
- role: user
content_file: ./today_generated_text.user.md
output:
format: json
validation_mode: json_schema
schema_path: today.generated_text.schema.json

View File

@@ -0,0 +1,30 @@
TASK: You are writing structured prose slots for a daily weather report.
The calling application will render the final Markdown report. Your job is not to write the full report. Return only a JSON object matching the configured schema.
Use only the supplied `data_package`. Do not invent weather details, times, hazards, probabilities, or impacts that are not supported by the data. The report focuses on the next civil day in `report.valid_period` for the configured location.
Return these fields:
- `summary`: required. One or two sentences summarizing the main weather story for the valid period.
- `forecast_discussion`: required. Three paragraphs explaining the broader setup, trend, or forecast reasoning most relevant to the valid period.
- `precipitation_timing`: optional. Include only when the deterministic `precip_timing` module contains precipitation windows.
- `confidence`: optional. Include only if uncertainty, timing spread, or conflicting signals materially affect how the reader should interpret the forecast.
Return JSON only.
# Summary
The summary should typically consist of two sentences. If an active warning is relevant during the report period, lead with the hazard. Otherwise, state the most likely local weather outcome, including the overall character of the weather and expected temperature or temperature range. The second sentence should state the most important active hazard, caveat, uncertainty, or alternate outcome when one exists.
Distinguish the main weather outcome from its caveat. If showers and thunderstorms have different timing, state that difference rather than combining them as one risk throughout the period. If the main caveat is a regional severe-weather or precipitation risk displaced from the report location, state that limitation clearly.
# Forecast discussion
Use narrative products to explain the “why” behind the local forecast when useful. Useful context may include synoptic pattern, fronts or boundaries, shortwaves, troughs or ridges, instability, moisture, shear, forcing, capping, regional placement of precipitation or severe-weather chances, hazards, timing windows, confidence, uncertainty, conditional outcomes, and relevant notes about following days.
In most cases, include three paragraphs: a two-to-four sentence relevant local or regional setup; a two-to-four sentence main uncertainty or conditional factor when present; and a two-to-four sentence next-day or broader-pattern note when supported.
# Precipitation timing
Use one or two sentences to give practical context about a supported frontal, convective, or stratiform setup; expected type, intensity, and duration; and uncertainty in onset or duration.

View File

@@ -0,0 +1,23 @@
id: weather.tomorrow_generated_text
version: "1.0.0"
default_profile: gemini-flash-latest
description: Tomorrow's weather report analysis prompt.
inputs:
- name: data_package
required: true
content_type: application/yaml
description: Structured weather data package
messages:
- role: system
content_file: ../common/system.md
- role: user
content_file: ../common/data_package.user.md
- role: user
content: |
{{input "data_package"}}
- role: user
content_file: ./tomorrow_generated_text.user.md
output:
format: json
validation_mode: json_schema
schema_path: tomorrow.generated_text.schema.json

View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "weatherreporter.daily.generated_text.schema.json",
"title": "Daily GeneratedText",
"type": "object",
"additionalProperties": false,
"required": ["summary", "forecast_discussion"],
"properties": {
"summary": {"type": "string"},
"forecast_discussion": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"precipitation_timing": {"type": "string"},
"confidence": {"type": "string"}
}
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "weatherreporter.hourly.generated_text.schema.json",
"title": "Hourly GeneratedText",
"type": "object",
"additionalProperties": false,
"required": ["summary", "forecast_discussion"],
"properties": {
"summary": {"type": "string"},
"forecast_discussion": {"type": "string"},
"precipitation_timing": {"type": "string"},
"confidence": {"type": "string"}
}
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "weatherreporter.today.generated_text.schema.json",
"title": "Today GeneratedText",
"type": "object",
"additionalProperties": false,
"required": ["summary", "forecast_discussion"],
"properties": {
"summary": {"type": "string"},
"forecast_discussion": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"precipitation_timing": {"type": "string"},
"confidence": {"type": "string"}
}
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "weatherreporter.tomorrow.generated_text.schema.json",
"title": "Tomorrow GeneratedText",
"type": "object",
"additionalProperties": false,
"required": ["summary", "forecast_discussion"],
"properties": {
"summary": {"type": "string"},
"forecast_discussion": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"precipitation_timing": {"type": "string"},
"confidence": {"type": "string"}
}
}

View File

@@ -0,0 +1,49 @@
// Package promptassets owns the embedded Promptkit prompt and schema corpus.
package promptassets
import (
"embed"
"fmt"
"io/fs"
)
//go:embed assets/prompts assets/schemas
var assets embed.FS
var schemaPaths = map[string]string{
"daily": "assets/schemas/daily.generated_text.schema.json",
"hourly": "assets/schemas/hourly.generated_text.schema.json",
"today": "assets/schemas/today.generated_text.schema.json",
"tomorrow": "assets/schemas/tomorrow.generated_text.schema.json",
}
// PromptFS returns the embedded prompt definitions and their referenced files.
func PromptFS() fs.FS {
fsys, err := fs.Sub(assets, "assets/prompts")
if err != nil {
panic(fmt.Sprintf("embedded prompt assets: %v", err))
}
return fsys
}
// SchemaFS returns the embedded generated-text JSON schemas.
func SchemaFS() fs.FS {
fsys, err := fs.Sub(assets, "assets/schemas")
if err != nil {
panic(fmt.Sprintf("embedded schema assets: %v", err))
}
return fsys
}
// Schema returns an independent copy of the canonical schema for id.
func Schema(id string) ([]byte, error) {
path, ok := schemaPaths[id]
if !ok {
return nil, fmt.Errorf("unknown generated text schema %q", id)
}
data, err := assets.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read generated text schema %q: %w", id, err)
}
return append([]byte(nil), data...), nil
}

View File

@@ -0,0 +1,161 @@
package promptassets_test
import (
"context"
"encoding/json"
"io/fs"
"strings"
"testing"
"gitea.maximumdirect.net/eric/promptkit"
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptassets"
"gopkg.in/yaml.v3"
)
type promptDefinition struct {
ID string `yaml:"id"`
Version string `yaml:"version"`
DefaultProfile string `yaml:"default_profile"`
Inputs []struct {
Name string `yaml:"name"`
Required bool `yaml:"required"`
ContentType string `yaml:"content_type"`
} `yaml:"inputs"`
Output struct {
Format string `yaml:"format"`
ValidationMode string `yaml:"validation_mode"`
SchemaPath string `yaml:"schema_path"`
RepairAttempts *int `yaml:"repair_attempts"`
} `yaml:"output"`
}
func TestPromptAssetsDeclareTheFourGeneratedTextPrompts(t *testing.T) {
tests := []struct {
path string
id string
schemaID string
}{
{"daily/daily_generated_text.yml", "weather.daily_generated_text", "daily"},
{"today/today_generated_text.yml", "weather.today_generated_text", "today"},
{"tomorrow/tomorrow_generated_text.yml", "weather.tomorrow_generated_text", "tomorrow"},
{"hourly/hourly_generated_text.yml", "weather.hourly_generated_text", "hourly"},
}
definitions := 0
if err := fs.WalkDir(promptassets.PromptFS(), ".", func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
if !entry.IsDir() && strings.HasSuffix(path, ".yml") {
definitions++
}
return nil
}); err != nil {
t.Fatalf("walk embedded prompts: %v", err)
}
if definitions != len(tests) {
t.Fatalf("prompt definitions = %d, want %d", definitions, len(tests))
}
for _, tc := range tests {
t.Run(tc.id, func(t *testing.T) {
data, err := fs.ReadFile(promptassets.PromptFS(), tc.path)
if err != nil {
t.Fatalf("read prompt definition: %v", err)
}
var definition promptDefinition
if err := yaml.Unmarshal(data, &definition); err != nil {
t.Fatalf("decode prompt definition: %v", err)
}
if definition.ID != tc.id || definition.Version != "1.0.0" || definition.DefaultProfile != "gemini-flash-latest" {
t.Fatalf("definition = %#v, want %s version 1.0.0 and gemini-flash-latest", definition, tc.id)
}
if len(definition.Inputs) != 1 || definition.Inputs[0].Name != "data_package" || !definition.Inputs[0].Required || definition.Inputs[0].ContentType != "application/yaml" {
t.Fatalf("inputs = %#v, want one required YAML data_package", definition.Inputs)
}
if definition.Output.Format != "json" || definition.Output.ValidationMode != "json_schema" || definition.Output.SchemaPath != tc.schemaID+".generated_text.schema.json" || definition.Output.RepairAttempts != nil {
t.Fatalf("output = %#v, want JSON schema output without repair attempts", definition.Output)
}
if _, err := promptassets.Schema(tc.schemaID); err != nil {
t.Fatalf("Schema(%q) error = %v", tc.schemaID, err)
}
})
}
}
func TestSchemasAreCanonicalAndIndependent(t *testing.T) {
for _, id := range []string{"daily", "today", "tomorrow", "hourly"} {
t.Run(id, func(t *testing.T) {
data, err := promptassets.Schema(id)
if err != nil {
t.Fatalf("Schema() error = %v", err)
}
var schema struct {
ID string `json:"$id"`
Title string `json:"title"`
Type string `json:"type"`
AdditionalProperties bool `json:"additionalProperties"`
Required []string `json:"required"`
Properties map[string]any `json:"properties"`
}
if err := json.Unmarshal(data, &schema); err != nil {
t.Fatalf("decode schema: %v", err)
}
if schema.Type != "object" || schema.AdditionalProperties || strings.Join(schema.Required, ",") != "summary,forecast_discussion" {
t.Fatalf("schema = %#v, want strict generated-text object", schema)
}
if _, ok := schema.Properties["confidence"]; !ok {
t.Fatalf("schema properties = %#v, want confidence", schema.Properties)
}
if id == "daily" && (schema.ID != "weatherreporter.daily.generated_text.schema.json" || schema.Title != "Daily GeneratedText") {
t.Fatalf("daily schema identity = %q/%q, want corrected Daily identity", schema.ID, schema.Title)
}
data[0] = 'x'
fresh, err := promptassets.Schema(id)
if err != nil || fresh[0] != '{' {
t.Fatalf("Schema() returned shared data or error: %v", err)
}
})
}
}
func TestPromptkitInspectsEmbeddedPromptsOffline(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{},
promptkit.WithPromptFS(promptassets.PromptFS(), "."),
promptkit.WithSchemaFS(promptassets.SchemaFS(), "."),
)
if err != nil {
t.Fatalf("NewEngine() error = %v", err)
}
for _, id := range []string{"weather.daily_generated_text", "weather.today_generated_text", "weather.tomorrow_generated_text", "weather.hourly_generated_text"} {
t.Run(id, func(t *testing.T) {
inspection, err := engine.InspectPrompt(context.Background(), id, "1.0.0")
if err != nil {
t.Fatalf("InspectPrompt() error = %v", err)
}
if inspection.PromptID != id || inspection.PromptVersion != "1.0.0" || inspection.DefaultProfileID != "gemini-flash-latest" {
t.Fatalf("inspection = %#v", inspection)
}
})
}
}
func TestPromptAssetsExcludeRetiredRuntimeSettings(t *testing.T) {
if err := fs.WalkDir(promptassets.PromptFS(), ".", func(path string, entry fs.DirEntry, err error) error {
if err != nil || entry.IsDir() {
return err
}
data, err := fs.ReadFile(promptassets.PromptFS(), path)
if err != nil {
return err
}
for _, unwanted := range []string{"local-heavy", "pipeline-weather/", "application/json", "repair_attempts:", "weather.daily_report"} {
if strings.Contains(string(data), unwanted) {
t.Fatalf("%s contains retired runtime setting %q", path, unwanted)
}
}
return nil
}); err != nil {
t.Fatalf("walk embedded prompts: %v", err)
}
}