From 88107d3168261cd788b84f7d4b0125b788ab89b3 Mon Sep 17 00:00:00 2001 From: acstrahl <58155309+acstrahl@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:48:31 -0800 Subject: [PATCH 1/2] Initial solution files --- .../campaign_generator.py | 166 ++++++++++++ .../conversation_manager.py | 248 ++++++++++++++++++ advanced-prompting-patterns/models.py | 25 ++ .../prompt_templates.py | 139 ++++++++++ advanced-prompting-patterns/test_prompts.py | 50 ++++ advanced-prompting-patterns/utils.py | 26 ++ 6 files changed, 654 insertions(+) create mode 100644 advanced-prompting-patterns/campaign_generator.py create mode 100644 advanced-prompting-patterns/conversation_manager.py create mode 100644 advanced-prompting-patterns/models.py create mode 100644 advanced-prompting-patterns/prompt_templates.py create mode 100644 advanced-prompting-patterns/test_prompts.py create mode 100644 advanced-prompting-patterns/utils.py diff --git a/advanced-prompting-patterns/campaign_generator.py b/advanced-prompting-patterns/campaign_generator.py new file mode 100644 index 0000000..d70dd0e --- /dev/null +++ b/advanced-prompting-patterns/campaign_generator.py @@ -0,0 +1,166 @@ +from conversation_manager import ConversationManager +from prompt_templates import build_campaign_prompt, build_extract_prompt, build_draft_prompt +from models import CampaignBrief, ExtractedProductInfo +from utils import validate_json_output + +def generate_campaign_brief(factsheet, max_retries=3): + """Generate a validated campaign brief with automatic repair.""" + + conversation = ConversationManager() + + # Use the template builder + initial_prompt = build_campaign_prompt(factsheet) + + response = conversation.chat_completion(initial_prompt) + + print("Initial response:") + print(response) + print() + + success, result = validate_json_output(response, CampaignBrief) + + # If valid on first try, return it + if success: + print("✓ Valid on first attempt!") + return result + + # Otherwise, try to repair + print(f"✗ Validation failed: {result}") + print() + + retries = 0 + while retries < max_retries: + print(f"Attempting repair {retries + 1}/{max_retries}...") + + repair_prompt = f""" +The JSON you provided had validation errors: + +{result} + +Please provide corrected JSON that fixes these errors. Remember: +- campaign_goal must be exactly "awareness", "engagement", or "conversion" +- All required fields must be present: campaign_name, target_audience, key_message, campaign_goal, call_to_action, channel_recommendations +- channel_recommendations must be a list of strings + +Respond with valid JSON only. +""" + + response = conversation.chat_completion(repair_prompt) + print("Repair response:") + print(response) + print() + + success, result = validate_json_output(response, CampaignBrief) + + if success: + print("✓ Repair successful!") + return result + + print(f"✗ Still invalid: {result}") + print() + retries += 1 + + # If we exhausted retries, raise an error + raise ValueError(f"Could not generate valid campaign brief after {max_retries} attempts. Last error: {result}") + +def extract_product_info(factsheet, max_retries=3): + """Extract structured information from product factsheet.""" + conversation = ConversationManager() + + prompt = build_extract_prompt(factsheet) + response = conversation.chat_completion(prompt) + success, result = validate_json_output(response, ExtractedProductInfo) + + if success: + return result + + # Simple repair loop (same pattern as before) + retries = 0 + while retries < max_retries: + repair_prompt = f"The JSON had errors: {result}\n\nProvide corrected JSON matching the required structure." + response = conversation.chat_completion(repair_prompt) + success, result = validate_json_output(response, ExtractedProductInfo) + if success: + return result + retries += 1 + + raise ValueError(f"Could not extract product info after {max_retries} attempts") + +def generate_campaign_brief_pipeline(factsheet, max_retries=3): + """Generate campaign brief using multi-step pipeline.""" + + # Step 1: Extract structured information + print("Step 1: Extracting product information...") + extracted = extract_product_info(factsheet, max_retries) + print(f"✓ Extracted info for: {extracted.product_name}") + + # Step 2: Draft campaign brief + print("Step 2: Drafting campaign brief...") + conversation = ConversationManager() + + prompt = build_draft_prompt(extracted) + response = conversation.chat_completion(prompt) + + print("Initial campaign brief response:") + print(response) + print() + + success, result = validate_json_output(response, CampaignBrief) + + if success: + print("✓ Valid campaign brief generated") + return result + + # Step 3: Repair if needed + print("Step 3: Repairing output...") + retries = 0 + while retries < max_retries: + repair_prompt = f""" +The JSON had validation errors: + +{result} + +Provide corrected JSON. Remember: +- campaign_goal must be "awareness", "engagement", or "conversion" +- All required fields must be present +- channel_recommendations must be a list of strings + +Respond with JSON only. +""" + response = conversation.chat_completion(repair_prompt) + success, result = validate_json_output(response, CampaignBrief) + + if success: + print("✓ Repair successful") + return result + retries += 1 + + raise ValueError(f"Could not generate valid brief after {max_retries} repair attempts") + +factsheet = """ +Product: Limited Edition Geisha Reserve +Origin: Hacienda La Esmeralda, Panama +Altitude: 1,600-1,800 meters +Processing: Natural, 72-hour fermentation +Flavor Profile: Jasmine, bergamot, white peach, honey sweetness, +silky body, complex finish with hints of tropical fruit +Certifications: Single Estate, Competition Grade +Limited Production: Only 500 bags produced this season +Story: This micro-lot scored 94.1 points in the 2024 Cup of Excellence +competition. The beans come from 30-year-old Geisha trees grown in +volcanic soil. The extended fermentation process was developed +specifically for this lot to enhance the floral characteristics. +Price: $89.99/bag +Previous Customer Feedback: "Best coffee I've ever tasted" - Coffee +Review Magazine. Sold out in 3 days last year. +""" + +try: + brief = generate_campaign_brief_pipeline(factsheet) + print("✓ Generated valid campaign brief!") + print(f"Campaign: {brief.campaign_name}") + print(f"Target: {brief.target_audience}") + print(f"Goal: {brief.campaign_goal.value}") + print(f"Channels: {', '.join(brief.channel_recommendations)}") +except ValueError as e: + print(f"✗ Failed to generate brief: {e}") diff --git a/advanced-prompting-patterns/conversation_manager.py b/advanced-prompting-patterns/conversation_manager.py new file mode 100644 index 0000000..5a300cc --- /dev/null +++ b/advanced-prompting-patterns/conversation_manager.py @@ -0,0 +1,248 @@ +import os +import tiktoken +import json +from openai import OpenAI +from datetime import datetime + +DEFAULT_API_KEY = os.environ.get("TOGETHER_API_KEY") +DEFAULT_BASE_URL = "https://api.together.xyz/v1" +DEFAULT_MODEL = "meta-llama/Meta-Llama-3-8B-Instruct-Lite" +DEFAULT_TEMPERATURE = 0.7 +DEFAULT_MAX_TOKENS = 350 +DEFAULT_TOKEN_BUDGET = 4096 + + +class ConversationManager: + def __init__(self, api_key=None, base_url=None, model=None, history_file=None, temperature=None, max_tokens=None, token_budget=None): + if not api_key: + api_key = DEFAULT_API_KEY + if not base_url: + base_url = DEFAULT_BASE_URL + + self.client = OpenAI( + api_key=api_key, + base_url=base_url + ) + if history_file is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + self.history_file = f"conversation_history_{timestamp}.json" + else: + self.history_file = history_file + + self.model = model if model else DEFAULT_MODEL + self.temperature = temperature if temperature else DEFAULT_TEMPERATURE + self.max_tokens = max_tokens if max_tokens else DEFAULT_MAX_TOKENS + self.token_budget = token_budget if token_budget else DEFAULT_TOKEN_BUDGET + + self.system_messages = { + "blogger": "You are a creative blogger specializing in engaging and informative content for GlobalJava Roasters.", + "social_media_expert": "You are a social media expert, crafting catchy and shareable posts for GlobalJava Roasters.", + "creative_assistant": "You are a creative assistant skilled in crafting engaging marketing content for GlobalJava Roasters.", + "custom": "Enter your custom system message here." + } + self.system_message = self.system_messages["creative_assistant"] + self.conversation_history = [{"role": "system", "content": self.get_system_message()}] + + def count_tokens(self, text): + try: + encoding = tiktoken.encoding_for_model(self.model) + except KeyError: + encoding = tiktoken.get_encoding("cl100k_base") + + tokens = encoding.encode(text) + return len(tokens) + + def total_tokens_used(self): + return sum(self.count_tokens(message['content']) for message in self.conversation_history) + + def enforce_token_budget(self): + while self.total_tokens_used() > self.token_budget: + if len(self.conversation_history) <= 1: + break + self.conversation_history.pop(1) + + def set_persona(self, persona): + if persona in self.system_messages: + self.system_message = self.system_messages[persona] + self.update_system_message_in_history() + else: + raise ValueError(f"Unknown persona: {persona}. Available personas are: {list(self.system_messages.keys())}") + + def set_custom_system_message(self, custom_message): + if not custom_message: + raise ValueError("Custom message cannot be empty.") + self.system_messages['custom'] = custom_message + self.set_persona('custom') + + def get_system_message(self): + system_message = self.system_message + system_message += f"\nImportant: Tailor your response to fit within {DEFAULT_MAX_TOKENS/2} word limit\n" + return system_message + + def update_system_message_in_history(self): + if self.conversation_history and self.conversation_history[0]["role"] == "system": + self.conversation_history[0]["content"] = self.get_system_message() + else: + system_message = self.system_message + system_message += f"\nImportant: Tailor your response to fit within {DEFAULT_MAX_TOKENS/2} words limit\n" + self.conversation_history.insert(0, {"role": "system", "content": self.get_system_message()}) + + def chat_completion(self, prompt, temperature=None, max_tokens=None): + temperature = temperature if temperature is not None else self.temperature + max_tokens = max_tokens if max_tokens is not None else self.max_tokens + + self.conversation_history.append({"role": "user", "content": prompt}) + + self.enforce_token_budget() + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=self.conversation_history, + temperature=temperature, + max_tokens=max_tokens, + ) + except Exception as e: + print(f"An error occurred while generating a response: {e}") + return None + + ai_response = response.choices[0].message.content + self.conversation_history.append({"role": "assistant", "content": ai_response}) + + return ai_response + + def reset_conversation_history(self): + self.conversation_history = [{"role": "system", "content": self.self.get_system_message()}] + + +section_one = """ + +European Union (EU) imports are dominated by unroasted green coffee beans, which accounts for about 90 percent of trade. Top suppliers in 2022/23 (October through September) included Brazil (32 percent), Vietnam (26 percent), Uganda (7 percent), and Honduras (6 percent). Imports reached a record 49.1 million bags the previous year but slipped 2.6 million bags this year as lower imports from Brazil more than offset gains from Vietnam. These two countries accounted for 54 to 58 percent of EU green coffee imports over the last 10 years, leaving limited market share for other suppliers. During this same period, Uganda gained 1.2 million bags to total 3.4 million on rising production while Colombia lost 500,000 bags to total 1.8 million on falling output. + +EU imports of roasted coffee totaled just 1.4 million bags in 2022/23, down from a record 2.1 million bags 4 years earlier on reduced imports from Switzerland. Top suppliers included Switzerland (77 percent) and the United Kingdom (13 percent). Because coffee beans begin to lose flavor and aroma shortly after being roasted, these imports are mostly limited to neighboring non-producing countries + +Imports of soluble coffee rebounded 300,000 bags to 3.7 million in 2022/23. Top suppliers included the United Kingdom (34 percent), Vietnam (12 percent), India (12 percent), and Ecuador (10 percent). While imports from the United Kingdom have been nearly flat at 1.3 million bags for a decade, imports from India and Vietnam gained about 300,000 bags to each total over 400,000. + +EU green coffee imports are forecast to rebound slightly in 2023/24 to 47.0 million bags primarily due to stronger shipments from Brazil, while roasted and soluble coffee imports remain flat at 1.4 million bags and 3.7 million bags, respectively. +""" + +section_two = """ +2023/24 Coffee Overview +World coffee production for 2023/24 is +forecast to reach 171.4 million bags (60 +kilograms), 6.9 million bags higher than +the previous year. Higher output in +Brazil, Colombia, and Ethiopia is +expected to more than offset reduced +production in Indonesia. Global coffee +bean exports are expected up 8.4 +million bags to 119.9 million, primarily +on strong shipments from Brazil. With +global consumption forecast at a record +169.5 million bags, ending inventories +are expected to continue to tighten to a +12-year low of 26.5 million bags. +Brazil combined Arabica and Robusta +harvest is forecast up 3.7 million bags to +66.3 million in 2023/24. Arabica output +is forecast to improve 5.1 million bags +to 44.9 million. In January 2023, coffee +trees in top growing region Minas +Gerais experienced higher than average +rains during the fruit development +stage, causing difficulties for some +growers in controlling plant diseases +and pests. However, increased +precipitation resulted in coarser and +heavier beans compared to the last +crop, which contributed to production +gains. Although output is expected to +expand, this quantity is below previous +crops that peaked at nearly 50.0 million bags. Arabica trees in many growing regions continue to recover +from severe weather that occurred in 2021 including severe frosts, high temperatures, and below- +average rainfall that lowered production in 2021/22 and 2022/23. Following 6 years of expansion, the +Robusta harvest is forecast to decline 1.4 million bags to 21.4 million as reduced precipitation and cooler +temperatures leading up to the flowering stage lowered yields in Espirito Santo, where the vast majority +is grown. Coffee bean exports are forecast to rebound 7.3 million bags to 39.5 million, fueled by higher +supplies and stronger EU and U.S. import demand. +Vietnam production is forecast to add 300,000 bags to reach 27.5 million. Cultivated area is forecast +unchanged, with nearly 95 percent of total output remaining as Robusta. However, with lower total +supplies due to last year’s stocks drawdown, bean exports are forecast to decline 2.4 million bags to +23.0 million. +Colombia Arabica production is forecast up 800,000 bags to 11.5 million on slightly higher yields. +However, yields remain nearly 15 percent below normal because growers limited fertilizer use due to +high prices. Bean exports, mostly to the United States and EU, are forecast up 1.2 million bags to 10.8 +million on strong demand. +Indonesia combined Arabica and +Robusta harvest is forecast down 2.2 +million bags to 9.7 million. Robusta +production is expected to drop 2.1 +million bags to 8.4 million. Excessive +rain during cherry development lowered +yields and caused sub-optimal +conditions for pollination in the lowland +areas of Southern Sumatra and Java, +where approximately 75 percent of +coffee is grown. Arabica production is +seen dipping slightly to 1.3 million bags. +Bean exports are forecast to plummet +2.7 million bags to 5.0 million on sharply +reduced supplies. +India combined Arabica and Robusta harvest is forecast nearly unchanged at 6.0 million bags. Arabica +production is forecast to drop 200,000 bags to 1.4 million due primarily to a prolonged dry spell from +December 2022 to March 2023 which was followed by poor pre-monsoon rains. Robusta production is +expected to rise 300,000 bags to 4.5 million on slightly higher yields. Bean exports are forecast up +300,000 bags to 4.3 million on a slight inventory drawdown. +""" + +section_three = """ +Revisions to 2022/23 Forecasts +World production is lowered 5.5 million bags from the June 2023 estimate to 164.5 million. +• Vietnam is 2.6 million bags lower to 27.2 million due to dry conditions that dropped yields. +• Ethiopia is reduced 1.0 million bags to 7.3 million as dry conditions lowered yields. +• Colombia is down 600,000 bags to 10.7 million as damaging rains fell in some areas during the +flowering period. +World bean exports are lowered 4.9 million bags to 111.6 million. +• Colombia is down 1.2 million bags to 9.6 million on lower output and higher ending stocks. +• Ethiopia is lowered 900,000 bags to 3.9 million on reduced output. +• Vietnam is reduced 600,000 bags to 25.4 million on lower output. +World ending stocks are revised down 4.0 million bags to 27.6 million. +• EU is down 3.3 million bags to 9.3 million on stronger-than-anticipated consumption. +• Vietnam is lowered 1.4 million bags to 300,000 on reduced production +""" + +section_one_summary = f""" +Section One Summary: + Here's a summary of the key points from the ICO coffee report on EU's coffee import trends: + +- The European Union is the world's largest coffee importer, mainly of unroasted green coffee beans, making up approximately 90% of their coffee trade. +- In the 2022/23 period, the leading suppliers of green coffee to the EU were Brazil (32%), Vietnam (26%), Uganda (7%), and Honduras (6%). +- EU's green coffee imports decreased by 2.6 million bags from the previous year's record of 49.1 million bags, primarily due to reduced imports from Brazil. +- Over the past decade, Brazil and Vietnam have consistently provided between 54% and 58% of the EU's green coffee imports, with other suppliers having a limited market share. +- Uganda's exports to the EU increased by 1.2 million bags due to higher production, while Colombia's exports decreased by 500,000 bags due to lower output. +- Roasted coffee imports in the EU dropped to 1.4 million bags in 2022/23, with the main suppliers being Switzerland (77%) and the United Kingdom (13%). +- Soluble coffee imports to the EU rose to 3.7 million bags, with the UK, Vietnam, India, and Ecuador being the primary suppliers. +- Green coffee imports are expected to slightly increase in 2023/24, with a forecast of 47.0 million bags, mainly due to expected higher shipments from Brazil. +- Imports of roasted and soluble coffee are predicted to remain stable at 1.4 million bags and 3.7 million bags, respectively. + +This summary encapsulates the major trends and projections regarding the European Union's coffee import habits as detailed in the ICO report. +""" + +section_two_summary = f""" +Section Two Summary: + Here's a summary of the 2023/24 Coffee Overview from the ICO report: + +- Global coffee production is forecast to rise to 171.4 million bags, an increase of 6.9 million bags from the previous year. +- Brazil, Colombia, and Ethiopia are expected to see higher outputs, compensating for a production decline in Indonesia. +- Worldwide coffee bean exports are projected to increase by 8.4 million bags to 119.9 million, largely driven by exports from Brazil. +- Consumption is forecast to hit a record 169.5 million bags, leading to the lowest ending inventories in 12 years, at 26.5 million bags. +- Brazil's total coffee harvest is expected to rise by 3.7 million bags to 66.3 million, with Arabica production up by 5.1 million bags despite previous weather challenges. +- Robusta production in Brazil is forecast to decrease by 1.4 million bags due to less favorable weather conditions. +- Brazil's coffee bean exports are expected to surge by 7.3 million bags to 39.5 million because of increased supply and demand from the EU and U.S. +- Vietnam's production is forecast to increase by 300,000 bags to 27.5 million, though exports may drop due to depleted stocks from the previous year. +- Colombia is anticipated to see an 800,000 bag increase in Arabica production, with exports growing due to strong demand. +- Indonesia's coffee harvest is projected to fall by 2.2 million bags, with exports expected to significantly decline. +- India's coffee harvest is expected to remain stable at 6.0 million bags, with a slight increase in Robusta offsetting a decrease in Arabica production. Exports are predicted to rise marginally. + +This summary encapsulates the projected trends and changes in coffee production, export, and consumption for the 2023/24 period as reported by the ICO. +""" \ No newline at end of file diff --git a/advanced-prompting-patterns/models.py b/advanced-prompting-patterns/models.py new file mode 100644 index 0000000..e249865 --- /dev/null +++ b/advanced-prompting-patterns/models.py @@ -0,0 +1,25 @@ +from pydantic import BaseModel +from typing import List +from enum import Enum + +class CampaignGoal(str, Enum): + AWARENESS = "awareness" + ENGAGEMENT = "engagement" + CONVERSION = "conversion" + +class CampaignBrief(BaseModel): + campaign_name: str + target_audience: str + key_message: str + campaign_goal: CampaignGoal + call_to_action: str + channel_recommendations: List[str] + +class ExtractedProductInfo(BaseModel): + product_name: str + origin_story: str + unique_features: List[str] + flavor_highlights: List[str] + certifications: List[str] + price_point: str + scarcity_factors: List[str] = [] \ No newline at end of file diff --git a/advanced-prompting-patterns/prompt_templates.py b/advanced-prompting-patterns/prompt_templates.py new file mode 100644 index 0000000..563b88d --- /dev/null +++ b/advanced-prompting-patterns/prompt_templates.py @@ -0,0 +1,139 @@ +CAMPAIGN_SYSTEM_V2 = """ +PROMPT_ID: campaign_brief_generator +VERSION: 2.0 +LAST_UPDATED: 2026-01-08 +CHANGELOG: +- v2.0: Added specific channel guidance for premium products +- v1.1: Clarified target audience requirements +- v1.0: Initial version + +You are a marketing campaign strategist for GlobalJava Roasters, +a premium coffee company focused on quality and sustainability. +""" + +CAMPAIGN_SYSTEM = """ +You are a marketing campaign strategist for GlobalJava Roasters, +a premium coffee company focused on quality and sustainability. +""" + +CAMPAIGN_TASK = """ +Create a marketing campaign brief for the product described below. +""" + +CAMPAIGN_CONSTRAINTS = """ +Follow these rules: +- Base all claims on the provided product information +- Use professional but engaging language appropriate for coffee enthusiasts +- Focus on the product's unique characteristics and value proposition +- Recommend marketing channels suitable for premium coffee consumers +""" + +CAMPAIGN_OUTPUT = """ +Output your response as valid JSON with this structure: + +{{ + "campaign_name": "string", + "target_audience": "string", + "key_message": "string", + "campaign_goal": "awareness" | "engagement" | "conversion", + "call_to_action": "string", + "channel_recommendations": ["string", "string", ...] +}} + +Respond with JSON only. No explanations or markdown. +""" + +def build_campaign_prompt(factsheet): + """Build a campaign brief prompt from template blocks.""" + reference = f"Product Information:\n{factsheet}" + + return f""" +{CAMPAIGN_SYSTEM} + +{CAMPAIGN_TASK} + +{CAMPAIGN_CONSTRAINTS} + +{reference} + +{CAMPAIGN_OUTPUT} +""".strip() + +EXTRACT_SYSTEM = "You are a data extraction specialist." + +EXTRACT_TASK = """ +Extract key marketing-relevant information from the product factsheet below. +Focus on facts that would matter for a marketing campaign. +""" + +EXTRACT_OUTPUT = """ +Output JSON with this structure: +{{ + "product_name": "string", + "origin_story": "string", + "unique_features": ["string", "string", ...], + "flavor_highlights": ["string", "string", ...], + "certifications": ["string", "string", ...], + "price_point": "budget" | "mid-range" | "premium" | "luxury", + "scarcity_factors": ["string", ...] or null +}} + +Respond with JSON only. +""" + +def build_extract_prompt(factsheet): + reference = f"Product Factsheet:\n{factsheet}" + return f""" +{EXTRACT_SYSTEM} + +{EXTRACT_TASK} + +{reference} + +{EXTRACT_OUTPUT} +""".strip() + + +DRAFT_CAMPAIGN_SYSTEM = """ +You are a marketing campaign strategist for GlobalJava Roasters. +""" + +DRAFT_CAMPAIGN_TASK = """ +Create a compelling marketing campaign brief using the extracted +product information provided below. +""" + +DRAFT_CAMPAIGN_CONSTRAINTS = """ +Guidelines: +- Craft a campaign name that captures the product's essence +- Target audience should reflect the price point and product characteristics +- Key message should highlight the most compelling unique features +- Choose an appropriate campaign goal based on product positioning +- Create a clear, actionable call-to-action +- Recommend channels that reach premium coffee enthusiasts +""" + +def build_draft_prompt(extracted_info): + # Format the extracted info nicely + info_text = f""" +Product: {extracted_info.product_name} +Origin Story: {extracted_info.origin_story} +Unique Features: {', '.join(extracted_info.unique_features)} +Flavor Highlights: {', '.join(extracted_info.flavor_highlights)} +Price Point: {extracted_info.price_point} +""" + if extracted_info.scarcity_factors: + info_text += f"Scarcity: {', '.join(extracted_info.scarcity_factors)}\n" + + return f""" +{DRAFT_CAMPAIGN_SYSTEM} + +{DRAFT_CAMPAIGN_TASK} + +{DRAFT_CAMPAIGN_CONSTRAINTS} + +Extracted Product Information: +{info_text} + +{CAMPAIGN_OUTPUT} +""".strip() \ No newline at end of file diff --git a/advanced-prompting-patterns/test_prompts.py b/advanced-prompting-patterns/test_prompts.py new file mode 100644 index 0000000..069e11b --- /dev/null +++ b/advanced-prompting-patterns/test_prompts.py @@ -0,0 +1,50 @@ +from campaign_generator import generate_campaign_brief +from models import CampaignGoal + +def test_basic_generation(): + """Can we generate a valid brief at all?""" + factsheet = """ + Product: House Blend Medium Roast + Origin: Colombia and Brazil blend + Flavor: Balanced, chocolatey, nutty + Price: $12.99/bag + """ + + brief = generate_campaign_brief(factsheet) + + # Basic assertions - it works and has required content + assert brief.campaign_name, "Campaign name missing" + assert len(brief.campaign_name) >= 5, "Campaign name too short" + assert brief.target_audience, "Target audience missing" + assert brief.campaign_goal in [g.value for g in CampaignGoal], "Invalid campaign goal" + assert len(brief.channel_recommendations) > 0, "No channels recommended" + + print("✓ Basic generation test passed") + +def test_premium_product_targeting(): + """Premium products should target sophisticated audiences.""" + factsheet = """ + Product: Single Origin Ethiopian Yirgacheffe + Origin: Gedeb region, Ethiopia + Processing: Washed + Flavor: Floral, citrus, tea-like + Certifications: Organic, Fair Trade + Price: $24.99/bag + """ + + brief = generate_campaign_brief(factsheet) + + # Premium products should use sophisticated targeting language + target = brief.target_audience.lower() + sophisticated_terms = ['enthusiast', 'connoisseur', 'specialty', 'premium', 'aficionado'] + + assert any(term in target for term in sophisticated_terms), \ + f"Premium product should target sophisticated audience, got: {brief.target_audience}" + + print("✓ Premium targeting test passed") + +if __name__ == "__main__": + print("Running prompt tests...\n") + test_basic_generation() + test_premium_product_targeting() + print("\n✓ All tests passed!") \ No newline at end of file diff --git a/advanced-prompting-patterns/utils.py b/advanced-prompting-patterns/utils.py new file mode 100644 index 0000000..d6132fb --- /dev/null +++ b/advanced-prompting-patterns/utils.py @@ -0,0 +1,26 @@ +import json +from pydantic import ValidationError + +def validate_json_output(response_text, model_class): + """ + Parse JSON from LLM response and validate against Pydantic model. + Returns (success, result_or_errors) + """ + try: + # Extract JSON (handles markdown code blocks if present) + json_text = response_text.strip() + if json_text.startswith("```json"): + json_text = json_text.split("```json")[1].split("```")[0].strip() + elif json_text.startswith("```"): + json_text = json_text.split("```")[1].split("```")[0].strip() + + # Parse and validate + data = json.loads(json_text) + validated = model_class(**data) + return True, validated + + except json.JSONDecodeError as e: + return False, f"JSON parsing error: {str(e)}" + + except ValidationError as e: + return False, f"Validation errors: {e.errors()}" \ No newline at end of file From f7018915bc5b9ef15af8da7fe22ce155293498ed Mon Sep 17 00:00:00 2001 From: acstrahl <58155309+acstrahl@users.noreply.github.com> Date: Fri, 9 Jan 2026 14:07:47 -0800 Subject: [PATCH 2/2] second pass of edits and restructuring --- .../conversation_manager.py | 131 ------------------ .../campaign_generator.py | 107 ++++++++------ .../solution-files/conversation_manager.py | 117 ++++++++++++++++ .../{ => solution-files}/models.py | 6 +- .../{ => solution-files}/prompt_templates.py | 4 +- .../{ => solution-files}/test_prompts.py | 6 +- .../{ => solution-files}/utils.py | 0 7 files changed, 187 insertions(+), 184 deletions(-) rename advanced-prompting-patterns/{ => solution-files}/campaign_generator.py (69%) create mode 100644 advanced-prompting-patterns/solution-files/conversation_manager.py rename advanced-prompting-patterns/{ => solution-files}/models.py (76%) rename advanced-prompting-patterns/{ => solution-files}/prompt_templates.py (95%) rename advanced-prompting-patterns/{ => solution-files}/test_prompts.py (85%) rename advanced-prompting-patterns/{ => solution-files}/utils.py (100%) diff --git a/advanced-prompting-patterns/conversation_manager.py b/advanced-prompting-patterns/conversation_manager.py index 5a300cc..53792d6 100644 --- a/advanced-prompting-patterns/conversation_manager.py +++ b/advanced-prompting-patterns/conversation_manager.py @@ -115,134 +115,3 @@ def reset_conversation_history(self): self.conversation_history = [{"role": "system", "content": self.self.get_system_message()}] -section_one = """ - -European Union (EU) imports are dominated by unroasted green coffee beans, which accounts for about 90 percent of trade. Top suppliers in 2022/23 (October through September) included Brazil (32 percent), Vietnam (26 percent), Uganda (7 percent), and Honduras (6 percent). Imports reached a record 49.1 million bags the previous year but slipped 2.6 million bags this year as lower imports from Brazil more than offset gains from Vietnam. These two countries accounted for 54 to 58 percent of EU green coffee imports over the last 10 years, leaving limited market share for other suppliers. During this same period, Uganda gained 1.2 million bags to total 3.4 million on rising production while Colombia lost 500,000 bags to total 1.8 million on falling output. - -EU imports of roasted coffee totaled just 1.4 million bags in 2022/23, down from a record 2.1 million bags 4 years earlier on reduced imports from Switzerland. Top suppliers included Switzerland (77 percent) and the United Kingdom (13 percent). Because coffee beans begin to lose flavor and aroma shortly after being roasted, these imports are mostly limited to neighboring non-producing countries - -Imports of soluble coffee rebounded 300,000 bags to 3.7 million in 2022/23. Top suppliers included the United Kingdom (34 percent), Vietnam (12 percent), India (12 percent), and Ecuador (10 percent). While imports from the United Kingdom have been nearly flat at 1.3 million bags for a decade, imports from India and Vietnam gained about 300,000 bags to each total over 400,000. - -EU green coffee imports are forecast to rebound slightly in 2023/24 to 47.0 million bags primarily due to stronger shipments from Brazil, while roasted and soluble coffee imports remain flat at 1.4 million bags and 3.7 million bags, respectively. -""" - -section_two = """ -2023/24 Coffee Overview -World coffee production for 2023/24 is -forecast to reach 171.4 million bags (60 -kilograms), 6.9 million bags higher than -the previous year. Higher output in -Brazil, Colombia, and Ethiopia is -expected to more than offset reduced -production in Indonesia. Global coffee -bean exports are expected up 8.4 -million bags to 119.9 million, primarily -on strong shipments from Brazil. With -global consumption forecast at a record -169.5 million bags, ending inventories -are expected to continue to tighten to a -12-year low of 26.5 million bags. -Brazil combined Arabica and Robusta -harvest is forecast up 3.7 million bags to -66.3 million in 2023/24. Arabica output -is forecast to improve 5.1 million bags -to 44.9 million. In January 2023, coffee -trees in top growing region Minas -Gerais experienced higher than average -rains during the fruit development -stage, causing difficulties for some -growers in controlling plant diseases -and pests. However, increased -precipitation resulted in coarser and -heavier beans compared to the last -crop, which contributed to production -gains. Although output is expected to -expand, this quantity is below previous -crops that peaked at nearly 50.0 million bags. Arabica trees in many growing regions continue to recover -from severe weather that occurred in 2021 including severe frosts, high temperatures, and below- -average rainfall that lowered production in 2021/22 and 2022/23. Following 6 years of expansion, the -Robusta harvest is forecast to decline 1.4 million bags to 21.4 million as reduced precipitation and cooler -temperatures leading up to the flowering stage lowered yields in Espirito Santo, where the vast majority -is grown. Coffee bean exports are forecast to rebound 7.3 million bags to 39.5 million, fueled by higher -supplies and stronger EU and U.S. import demand. -Vietnam production is forecast to add 300,000 bags to reach 27.5 million. Cultivated area is forecast -unchanged, with nearly 95 percent of total output remaining as Robusta. However, with lower total -supplies due to last year’s stocks drawdown, bean exports are forecast to decline 2.4 million bags to -23.0 million. -Colombia Arabica production is forecast up 800,000 bags to 11.5 million on slightly higher yields. -However, yields remain nearly 15 percent below normal because growers limited fertilizer use due to -high prices. Bean exports, mostly to the United States and EU, are forecast up 1.2 million bags to 10.8 -million on strong demand. -Indonesia combined Arabica and -Robusta harvest is forecast down 2.2 -million bags to 9.7 million. Robusta -production is expected to drop 2.1 -million bags to 8.4 million. Excessive -rain during cherry development lowered -yields and caused sub-optimal -conditions for pollination in the lowland -areas of Southern Sumatra and Java, -where approximately 75 percent of -coffee is grown. Arabica production is -seen dipping slightly to 1.3 million bags. -Bean exports are forecast to plummet -2.7 million bags to 5.0 million on sharply -reduced supplies. -India combined Arabica and Robusta harvest is forecast nearly unchanged at 6.0 million bags. Arabica -production is forecast to drop 200,000 bags to 1.4 million due primarily to a prolonged dry spell from -December 2022 to March 2023 which was followed by poor pre-monsoon rains. Robusta production is -expected to rise 300,000 bags to 4.5 million on slightly higher yields. Bean exports are forecast up -300,000 bags to 4.3 million on a slight inventory drawdown. -""" - -section_three = """ -Revisions to 2022/23 Forecasts -World production is lowered 5.5 million bags from the June 2023 estimate to 164.5 million. -• Vietnam is 2.6 million bags lower to 27.2 million due to dry conditions that dropped yields. -• Ethiopia is reduced 1.0 million bags to 7.3 million as dry conditions lowered yields. -• Colombia is down 600,000 bags to 10.7 million as damaging rains fell in some areas during the -flowering period. -World bean exports are lowered 4.9 million bags to 111.6 million. -• Colombia is down 1.2 million bags to 9.6 million on lower output and higher ending stocks. -• Ethiopia is lowered 900,000 bags to 3.9 million on reduced output. -• Vietnam is reduced 600,000 bags to 25.4 million on lower output. -World ending stocks are revised down 4.0 million bags to 27.6 million. -• EU is down 3.3 million bags to 9.3 million on stronger-than-anticipated consumption. -• Vietnam is lowered 1.4 million bags to 300,000 on reduced production -""" - -section_one_summary = f""" -Section One Summary: - Here's a summary of the key points from the ICO coffee report on EU's coffee import trends: - -- The European Union is the world's largest coffee importer, mainly of unroasted green coffee beans, making up approximately 90% of their coffee trade. -- In the 2022/23 period, the leading suppliers of green coffee to the EU were Brazil (32%), Vietnam (26%), Uganda (7%), and Honduras (6%). -- EU's green coffee imports decreased by 2.6 million bags from the previous year's record of 49.1 million bags, primarily due to reduced imports from Brazil. -- Over the past decade, Brazil and Vietnam have consistently provided between 54% and 58% of the EU's green coffee imports, with other suppliers having a limited market share. -- Uganda's exports to the EU increased by 1.2 million bags due to higher production, while Colombia's exports decreased by 500,000 bags due to lower output. -- Roasted coffee imports in the EU dropped to 1.4 million bags in 2022/23, with the main suppliers being Switzerland (77%) and the United Kingdom (13%). -- Soluble coffee imports to the EU rose to 3.7 million bags, with the UK, Vietnam, India, and Ecuador being the primary suppliers. -- Green coffee imports are expected to slightly increase in 2023/24, with a forecast of 47.0 million bags, mainly due to expected higher shipments from Brazil. -- Imports of roasted and soluble coffee are predicted to remain stable at 1.4 million bags and 3.7 million bags, respectively. - -This summary encapsulates the major trends and projections regarding the European Union's coffee import habits as detailed in the ICO report. -""" - -section_two_summary = f""" -Section Two Summary: - Here's a summary of the 2023/24 Coffee Overview from the ICO report: - -- Global coffee production is forecast to rise to 171.4 million bags, an increase of 6.9 million bags from the previous year. -- Brazil, Colombia, and Ethiopia are expected to see higher outputs, compensating for a production decline in Indonesia. -- Worldwide coffee bean exports are projected to increase by 8.4 million bags to 119.9 million, largely driven by exports from Brazil. -- Consumption is forecast to hit a record 169.5 million bags, leading to the lowest ending inventories in 12 years, at 26.5 million bags. -- Brazil's total coffee harvest is expected to rise by 3.7 million bags to 66.3 million, with Arabica production up by 5.1 million bags despite previous weather challenges. -- Robusta production in Brazil is forecast to decrease by 1.4 million bags due to less favorable weather conditions. -- Brazil's coffee bean exports are expected to surge by 7.3 million bags to 39.5 million because of increased supply and demand from the EU and U.S. -- Vietnam's production is forecast to increase by 300,000 bags to 27.5 million, though exports may drop due to depleted stocks from the previous year. -- Colombia is anticipated to see an 800,000 bag increase in Arabica production, with exports growing due to strong demand. -- Indonesia's coffee harvest is projected to fall by 2.2 million bags, with exports expected to significantly decline. -- India's coffee harvest is expected to remain stable at 6.0 million bags, with a slight increase in Robusta offsetting a decrease in Arabica production. Exports are predicted to rise marginally. - -This summary encapsulates the projected trends and changes in coffee production, export, and consumption for the 2023/24 period as reported by the ICO. -""" \ No newline at end of file diff --git a/advanced-prompting-patterns/campaign_generator.py b/advanced-prompting-patterns/solution-files/campaign_generator.py similarity index 69% rename from advanced-prompting-patterns/campaign_generator.py rename to advanced-prompting-patterns/solution-files/campaign_generator.py index d70dd0e..b70549f 100644 --- a/advanced-prompting-patterns/campaign_generator.py +++ b/advanced-prompting-patterns/solution-files/campaign_generator.py @@ -3,15 +3,14 @@ from models import CampaignBrief, ExtractedProductInfo from utils import validate_json_output +TEMPERATURE = 0.2 + def generate_campaign_brief(factsheet, max_retries=3): """Generate a validated campaign brief with automatic repair.""" - conversation = ConversationManager() - # Use the template builder initial_prompt = build_campaign_prompt(factsheet) - - response = conversation.chat_completion(initial_prompt) + response = conversation.chat_completion(initial_prompt, temperature=TEMPERATURE) print("Initial response:") print(response) @@ -19,125 +18,143 @@ def generate_campaign_brief(factsheet, max_retries=3): success, result = validate_json_output(response, CampaignBrief) - # If valid on first try, return it if success: print("✓ Valid on first attempt!") return result - # Otherwise, try to repair print(f"✗ Validation failed: {result}") print() - + retries = 0 + last_error = result + while retries < max_retries: print(f"Attempting repair {retries + 1}/{max_retries}...") - + repair_prompt = f""" The JSON you provided had validation errors: -{result} +{last_error} Please provide corrected JSON that fixes these errors. Remember: - campaign_goal must be exactly "awareness", "engagement", or "conversion" - All required fields must be present: campaign_name, target_audience, key_message, campaign_goal, call_to_action, channel_recommendations - channel_recommendations must be a list of strings -Respond with valid JSON only. +Respond with valid JSON only. Keep each string value to 1–2 sentences max. """ - response = conversation.chat_completion(repair_prompt) + response = conversation.chat_completion(repair_prompt, temperature=TEMPERATURE) + print("Repair response:") print(response) print() - + success, result = validate_json_output(response, CampaignBrief) if success: print("✓ Repair successful!") return result - - print(f"✗ Still invalid: {result}") + + last_error = result + print(f"✗ Still invalid: {last_error}") print() retries += 1 - # If we exhausted retries, raise an error - raise ValueError(f"Could not generate valid campaign brief after {max_retries} attempts. Last error: {result}") + raise ValueError( + f"Could not generate valid campaign brief after {max_retries} attempts. Last error: {last_error}" + ) + def extract_product_info(factsheet, max_retries=3): """Extract structured information from product factsheet.""" conversation = ConversationManager() prompt = build_extract_prompt(factsheet) - response = conversation.chat_completion(prompt) - success, result = validate_json_output(response, ExtractedProductInfo) + response = conversation.chat_completion(prompt, temperature=TEMPERATURE) + success, result = validate_json_output(response, ExtractedProductInfo) if success: return result - # Simple repair loop (same pattern as before) retries = 0 + last_error = result + while retries < max_retries: - repair_prompt = f"The JSON had errors: {result}\n\nProvide corrected JSON matching the required structure." - response = conversation.chat_completion(repair_prompt) + repair_prompt = f""" +The JSON had errors: + +{last_error} + +Provide corrected JSON matching the required structure. +Respond with JSON only. Keep each string value to 1–2 sentences max. +Use [] for scarcity_factors if none. +""" + response = conversation.chat_completion(repair_prompt, temperature=TEMPERATURE) + success, result = validate_json_output(response, ExtractedProductInfo) if success: return result + + last_error = result retries += 1 - raise ValueError(f"Could not extract product info after {max_retries} attempts") + raise ValueError(f"Could not extract product info after {max_retries} attempts. Last error: {last_error}") + def generate_campaign_brief_pipeline(factsheet, max_retries=3): """Generate campaign brief using multi-step pipeline.""" - - # Step 1: Extract structured information print("Step 1: Extracting product information...") extracted = extract_product_info(factsheet, max_retries) print(f"✓ Extracted info for: {extracted.product_name}") - # Step 2: Draft campaign brief print("Step 2: Drafting campaign brief...") conversation = ConversationManager() prompt = build_draft_prompt(extracted) - response = conversation.chat_completion(prompt) + response = conversation.chat_completion(prompt, temperature=TEMPERATURE) print("Initial campaign brief response:") print(response) print() success, result = validate_json_output(response, CampaignBrief) - if success: print("✓ Valid campaign brief generated") return result - # Step 3: Repair if needed print("Step 3: Repairing output...") retries = 0 + last_error = result + while retries < max_retries: repair_prompt = f""" The JSON had validation errors: -{result} +{last_error} Provide corrected JSON. Remember: - campaign_goal must be "awareness", "engagement", or "conversion" - All required fields must be present - channel_recommendations must be a list of strings -Respond with JSON only. +Respond with JSON only. Keep each string value to 1–2 sentences max. """ - response = conversation.chat_completion(repair_prompt) - success, result = validate_json_output(response, CampaignBrief) + response = conversation.chat_completion(repair_prompt, temperature=TEMPERATURE) + success, result = validate_json_output(response, CampaignBrief) if success: print("✓ Repair successful") return result + + last_error = result retries += 1 - raise ValueError(f"Could not generate valid brief after {max_retries} repair attempts") + raise ValueError(f"Could not generate valid brief after {max_retries} repair attempts. Last error: {last_error}") -factsheet = """ + +if __name__ == "__main__": + factsheet = """ Product: Limited Edition Geisha Reserve Origin: Hacienda La Esmeralda, Panama Altitude: 1,600-1,800 meters @@ -153,14 +170,14 @@ def generate_campaign_brief_pipeline(factsheet, max_retries=3): Price: $89.99/bag Previous Customer Feedback: "Best coffee I've ever tasted" - Coffee Review Magazine. Sold out in 3 days last year. -""" - -try: - brief = generate_campaign_brief_pipeline(factsheet) - print("✓ Generated valid campaign brief!") - print(f"Campaign: {brief.campaign_name}") - print(f"Target: {brief.target_audience}") - print(f"Goal: {brief.campaign_goal.value}") - print(f"Channels: {', '.join(brief.channel_recommendations)}") -except ValueError as e: - print(f"✗ Failed to generate brief: {e}") +""".strip() + + try: + brief = generate_campaign_brief_pipeline(factsheet) + print("✓ Generated valid campaign brief!") + print(f"Campaign: {brief.campaign_name}") + print(f"Target: {brief.target_audience}") + print(f"Goal: {brief.campaign_goal.value}") + print(f"Channels: {', '.join(brief.channel_recommendations)}") + except ValueError as e: + print(f"✗ Failed to generate brief: {e}") diff --git a/advanced-prompting-patterns/solution-files/conversation_manager.py b/advanced-prompting-patterns/solution-files/conversation_manager.py new file mode 100644 index 0000000..53792d6 --- /dev/null +++ b/advanced-prompting-patterns/solution-files/conversation_manager.py @@ -0,0 +1,117 @@ +import os +import tiktoken +import json +from openai import OpenAI +from datetime import datetime + +DEFAULT_API_KEY = os.environ.get("TOGETHER_API_KEY") +DEFAULT_BASE_URL = "https://api.together.xyz/v1" +DEFAULT_MODEL = "meta-llama/Meta-Llama-3-8B-Instruct-Lite" +DEFAULT_TEMPERATURE = 0.7 +DEFAULT_MAX_TOKENS = 350 +DEFAULT_TOKEN_BUDGET = 4096 + + +class ConversationManager: + def __init__(self, api_key=None, base_url=None, model=None, history_file=None, temperature=None, max_tokens=None, token_budget=None): + if not api_key: + api_key = DEFAULT_API_KEY + if not base_url: + base_url = DEFAULT_BASE_URL + + self.client = OpenAI( + api_key=api_key, + base_url=base_url + ) + if history_file is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + self.history_file = f"conversation_history_{timestamp}.json" + else: + self.history_file = history_file + + self.model = model if model else DEFAULT_MODEL + self.temperature = temperature if temperature else DEFAULT_TEMPERATURE + self.max_tokens = max_tokens if max_tokens else DEFAULT_MAX_TOKENS + self.token_budget = token_budget if token_budget else DEFAULT_TOKEN_BUDGET + + self.system_messages = { + "blogger": "You are a creative blogger specializing in engaging and informative content for GlobalJava Roasters.", + "social_media_expert": "You are a social media expert, crafting catchy and shareable posts for GlobalJava Roasters.", + "creative_assistant": "You are a creative assistant skilled in crafting engaging marketing content for GlobalJava Roasters.", + "custom": "Enter your custom system message here." + } + self.system_message = self.system_messages["creative_assistant"] + self.conversation_history = [{"role": "system", "content": self.get_system_message()}] + + def count_tokens(self, text): + try: + encoding = tiktoken.encoding_for_model(self.model) + except KeyError: + encoding = tiktoken.get_encoding("cl100k_base") + + tokens = encoding.encode(text) + return len(tokens) + + def total_tokens_used(self): + return sum(self.count_tokens(message['content']) for message in self.conversation_history) + + def enforce_token_budget(self): + while self.total_tokens_used() > self.token_budget: + if len(self.conversation_history) <= 1: + break + self.conversation_history.pop(1) + + def set_persona(self, persona): + if persona in self.system_messages: + self.system_message = self.system_messages[persona] + self.update_system_message_in_history() + else: + raise ValueError(f"Unknown persona: {persona}. Available personas are: {list(self.system_messages.keys())}") + + def set_custom_system_message(self, custom_message): + if not custom_message: + raise ValueError("Custom message cannot be empty.") + self.system_messages['custom'] = custom_message + self.set_persona('custom') + + def get_system_message(self): + system_message = self.system_message + system_message += f"\nImportant: Tailor your response to fit within {DEFAULT_MAX_TOKENS/2} word limit\n" + return system_message + + def update_system_message_in_history(self): + if self.conversation_history and self.conversation_history[0]["role"] == "system": + self.conversation_history[0]["content"] = self.get_system_message() + else: + system_message = self.system_message + system_message += f"\nImportant: Tailor your response to fit within {DEFAULT_MAX_TOKENS/2} words limit\n" + self.conversation_history.insert(0, {"role": "system", "content": self.get_system_message()}) + + def chat_completion(self, prompt, temperature=None, max_tokens=None): + temperature = temperature if temperature is not None else self.temperature + max_tokens = max_tokens if max_tokens is not None else self.max_tokens + + self.conversation_history.append({"role": "user", "content": prompt}) + + self.enforce_token_budget() + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=self.conversation_history, + temperature=temperature, + max_tokens=max_tokens, + ) + except Exception as e: + print(f"An error occurred while generating a response: {e}") + return None + + ai_response = response.choices[0].message.content + self.conversation_history.append({"role": "assistant", "content": ai_response}) + + return ai_response + + def reset_conversation_history(self): + self.conversation_history = [{"role": "system", "content": self.self.get_system_message()}] + + diff --git a/advanced-prompting-patterns/models.py b/advanced-prompting-patterns/solution-files/models.py similarity index 76% rename from advanced-prompting-patterns/models.py rename to advanced-prompting-patterns/solution-files/models.py index e249865..bd02ff8 100644 --- a/advanced-prompting-patterns/models.py +++ b/advanced-prompting-patterns/solution-files/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from typing import List from enum import Enum @@ -8,6 +8,7 @@ class CampaignGoal(str, Enum): CONVERSION = "conversion" class CampaignBrief(BaseModel): + model_config = ConfigDict(extra="forbid") campaign_name: str target_audience: str key_message: str @@ -16,10 +17,11 @@ class CampaignBrief(BaseModel): channel_recommendations: List[str] class ExtractedProductInfo(BaseModel): + model_config = ConfigDict(extra="forbid") product_name: str origin_story: str unique_features: List[str] flavor_highlights: List[str] certifications: List[str] price_point: str - scarcity_factors: List[str] = [] \ No newline at end of file + scarcity_factors: List[str] = [] diff --git a/advanced-prompting-patterns/prompt_templates.py b/advanced-prompting-patterns/solution-files/prompt_templates.py similarity index 95% rename from advanced-prompting-patterns/prompt_templates.py rename to advanced-prompting-patterns/solution-files/prompt_templates.py index 563b88d..313bab3 100644 --- a/advanced-prompting-patterns/prompt_templates.py +++ b/advanced-prompting-patterns/solution-files/prompt_templates.py @@ -40,7 +40,7 @@ "channel_recommendations": ["string", "string", ...] }} -Respond with JSON only. No explanations or markdown. +Respond with JSON only. No explanations or markdown. Keep each string value to 1–2 sentences max. """ def build_campaign_prompt(factsheet): @@ -75,7 +75,7 @@ def build_campaign_prompt(factsheet): "flavor_highlights": ["string", "string", ...], "certifications": ["string", "string", ...], "price_point": "budget" | "mid-range" | "premium" | "luxury", - "scarcity_factors": ["string", ...] or null + "scarcity_factors": ["string", ...] // use [] if none }} Respond with JSON only. diff --git a/advanced-prompting-patterns/test_prompts.py b/advanced-prompting-patterns/solution-files/test_prompts.py similarity index 85% rename from advanced-prompting-patterns/test_prompts.py rename to advanced-prompting-patterns/solution-files/test_prompts.py index 069e11b..ee61373 100644 --- a/advanced-prompting-patterns/test_prompts.py +++ b/advanced-prompting-patterns/solution-files/test_prompts.py @@ -12,11 +12,10 @@ def test_basic_generation(): brief = generate_campaign_brief(factsheet) - # Basic assertions - it works and has required content assert brief.campaign_name, "Campaign name missing" assert len(brief.campaign_name) >= 5, "Campaign name too short" assert brief.target_audience, "Target audience missing" - assert brief.campaign_goal in [g.value for g in CampaignGoal], "Invalid campaign goal" + assert brief.campaign_goal in CampaignGoal, "Invalid campaign goal" assert len(brief.channel_recommendations) > 0, "No channels recommended" print("✓ Basic generation test passed") @@ -34,7 +33,6 @@ def test_premium_product_targeting(): brief = generate_campaign_brief(factsheet) - # Premium products should use sophisticated targeting language target = brief.target_audience.lower() sophisticated_terms = ['enthusiast', 'connoisseur', 'specialty', 'premium', 'aficionado'] @@ -47,4 +45,4 @@ def test_premium_product_targeting(): print("Running prompt tests...\n") test_basic_generation() test_premium_product_targeting() - print("\n✓ All tests passed!") \ No newline at end of file + print("\n✓ All tests passed!") diff --git a/advanced-prompting-patterns/utils.py b/advanced-prompting-patterns/solution-files/utils.py similarity index 100% rename from advanced-prompting-patterns/utils.py rename to advanced-prompting-patterns/solution-files/utils.py