GPT-5.2 एक मजबूत मॉडल है जो ऐसे वर्कफ़्लो और मल्टी-स्टेप पाइपलाइनों को जनरेट करने में सक्षम है जिन्हें विश्वसनीयता, संरचना और कम hallucinations की जरूरत होती है। OpenAI ने long-context कार्य और स्प्रेडशीट-स्टाइल टास्क पर बेहतर परफॉर्मेंस को हाईलाइट किया है।
इस ट्यूटोरियल में, हम एक Streamlit ऐप बनाएंगे जो एक जूनियर एनालिस्ट की तरह काम करता है:
एक CSV अपलोड करें और लक्ष्य लिखें (उदाहरण: “ग्रोथ, रिटेंशन, यूनिट इकॉनॉमिक्स, रनवे पर फोकस करते हुए 6-स्लाइड अपडेट”)
एक schema-locked JSON प्लान जनरेट करें
कच्चे डेटा, derived metrics और चार्ट्स के साथ एक Excel वर्कबुक तैयार करें
अंत में, उपयुक्त citations के साथ एक मिलती-जुलती PowerPoint डेक जनरेट करें
मुख्य बात यह है कि GPT-5.2 सीधे “स्लाइड्स नहीं बनाता।” यह एक सख्त प्लान बनाता है जिसे आपका कोड नियतात्मक रूप से निष्पादित करता है।
GPT-5.2 OpenAI का नवीनतम GPT-5-सीरीज़ मॉडल है, जिसमें स्प्रेडशीट बनाना, प्रेजेंटेशन तैयार करना, कोड लिखना और डीबग करना, लंबे कॉन्टेक्स्ट को समझना और टूल्स का उपयोग करना जैसे end-to-end कार्यों के लिए सुधार शामिल हैं।
इस प्रोजेक्ट के लिए GPT-5.2 की प्रमुख विशेषताएँ:
लंबे संदर्भ पर तर्क: GPT-5.2 Thinking को long-context reasoning evals (OpenAI MRCRv2) में अत्याधुनिक बताया गया है, जिसका व्यावहारिक असर बहुत लंबे दस्तावेज़ों और मल्टी-फाइल इनपुट्स पर सुसंगत तरीके से काम करने में दिखता है।
मल्टी-स्टेप निष्पादन: यह मॉडल जटिल, बहु-चरणीय प्रोजेक्ट्स में बेहतर है—मजबूत टूल-कॉलिंग और long-horizon व्यवहार के साथ—जो सीधे हमारे schema-locked प्लानिंग और नियतात्मक रेंडरिंग लूप पर मैप होता है।
तथ्यपरकता में सुधार: GPT-5.2 Thinking, GPT-5.1 Thinking की तुलना में कम त्रुटिपूर्ण जवाब देता है, जो यहां सहायक है क्योंकि इससे हमारे उद्धृत Excel रेंज से भटकते हुए टेक्स्ट/नंबर जनरेट होने की संभावना घटती है।
लेटेंसी ट्रेडऑफ: API में, आप तेज़ फीडबैक के लिए GPT-5.2 Instant के साथ इटरेट कर सकते हैं, फिर जब आपको मजबूत end-to-end प्लानिंग और उच्च-गुणवत्ता वाले आर्टिफैक्ट्स चाहिए हों, तो GPT-5.2 Thinking या Pro पर स्विच करें—ध्यान रखें कि अधिक जटिल जनरेशन में मिनट लग सकते हैं।
GPT-5.2 उदाहरण प्रोजेक्ट: Structured Artifacts Generator बनाएं
इस सेक्शन में, हम Streamlit ऐप में रैप किए गए GPT 5.2 मॉडल का उपयोग करके एक structured artifacts जनरेटर (Excel और PowerPoint) बनाएंगे। उच्च स्तर पर, अंतिम Streamlit ऐप यह करता है:
यह CSV पढ़ता है और एक canonical वर्कबुक बनाता है
फिर, यह Structured Outputs (response_format: json_schema) के साथ OpenAI या OpenRouter के जरिए GPT-5.2 को कॉल करता है
वापस आए प्लान को हमारे स्कीमा के अनुसार वैलिडेट करता है
Excel में चार्ट्स रेंडर करता है और उन्हें हमारे ब्रांड पैलेट के अनुरूप स्टाइल करता है
अंत में, citations सहित embedded चार्ट्स के साथ एक PowerPoint जनरेट करता है
इसे चरण-दर-चरण बनाते हैं।
चरण 1: पूर्वापेक्षाएँ और सेटअप
GPT-5.2 के साथ Excel वर्कबुक और PowerPoint डेक जनरेट करने से पहले, हमें एक लोकल एनवायरनमेंट सेट करना होगा। हम कोर लाइब्रेरीज़ इंस्टॉल करने और API keys को environment variables के रूप में सेट करने से शुरू करेंगे।
pip install streamlit pandas pydantic python-dotenv requests openpyxl python-pptxसंचालित द्वारा
हम Streamlit का उपयोग इंटरैक्टिव वेब UI बनाने के लिए करेंगे, Pandas से CSV लोड और तैयार करेंगे, Pydantic से GPT-5.2 के schema-locked JSON प्लान को वैलिडेट करेंगे। हम python-dotenv का उपयोग .env फ़ाइल से OPENROUTER_API_KEY/OPENAI_API_KEY जैसी keys को सुरक्षित रूप से लोड करने के लिए भी करेंगे। requests लाइब्रेरी OpenRouter/OpenAI-समर्थित endpoint को कॉल करती है, जबकि openpyxl Excel वर्कबुक और चार्ट्स जनरेट करता है, और python-pptx native (editable) चार्ट्स के साथ PowerPoint डेक बनाता है।
अगला, हम अपनी API key/s सेट करते हैं:
exportOPENAI_API_KEY="your_key"exportOPENROUTER_API_KEY="your_key"संचालित द्वारा
ध्यान दें कि GPT 5.2 को कई सेवाओं के माध्यम से एक्सेस किया जा सकता है, जिसमें OpenAI का आधिकारिक API तथा OpenRouter जैसी सेवाएँ शामिल हैं।
API keys को लोकली स्टोर करने के लिए .env फ़ाइल का उपयोग करने की सिफारिश की जाती है:
.env फ़ाइल में निम्नलिखित सेव करें:
OPENROUTER_API_KEY="your_key"OPENAI_API_KEY="your_key"संचालित द्वारा
इसके बाद, इन API keys को load_dotenv लाइब्रेरी का उपयोग करके लोड करें:
from dotenv import load_dotenv
load_dotenv()संचालित द्वारा
इस बिंदु पर, हमारा एनवायरनमेंट OpenAI API के साथ प्रमाणित होने के लिए तैयार है।
चरण 2: प्लान स्कीमा परिभाषित करना
अब, हम एक schema-locked artifact प्लान परिभाषित करते हैं जो एक सख्त JSON ब्लूप्रिंट है—यह GPT-5.2 को बताता है कि कौन से चार्ट बनाने हैं और स्लाइड्स को कैसे संरचित करना है, ताकि मॉडल का आउटपुट मुक्त-रूप टेक्स्ट के बजाय response_format: { type: "json_schema", strict: true } के जरिए Structured Outputs का उपयोग करके डिकोड और वैलिडेट किया जा सके।
import os
import io
import re
import json
import math
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Literal, Optional, Tuple
import requests
import pandas as pd
import numpy as np
import streamlit as st
import openpyxl
from dotenv import load_dotenv
from pydantic import BaseModel, Field, ValidationError
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Font, Alignment, PatternFill
from openpyxl.utils import get_column_letter
from openpyxl.chart import LineChart, BarChart, Reference
from openpyxl.chart.series import SeriesLabel
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE, XL_LEGEND_POSITION
ChartKind = Literal["line","bar"]defsafe_float(x)->float:try:if x isNone:return0.0ifisinstance(x,(int,float)):returnfloat(x)returnfloat(str(x).replace(",","").replace("$",""))except Exception:return0.0deffmt_money(v:float)->str:
sign ="-"if v <0else""
v =abs(v)if v >=1_000_000:returnf"{sign}${v/1_000_000:.2f}M"if v >=1_000:returnf"{sign}${v/1_000:.1f}K"returnf"{sign}${v:.0f}"deffmt_pct(v:float)->str:if v isNoneor(isinstance(v,float)and(math.isnan(v)or math.isinf(v))):return"n/a"returnf"{v*100:.1f}%"classChartSeriesSpec(BaseModel):
name:str
metric:strclassChartSpec(BaseModel):id:str
kind: ChartKind
title:str
x_metric:str
series: List[ChartSeriesSpec]
anchor:strclassSlideBullet(BaseModel):
text:str
citations: List[str]= Field(min_length=1, max_length=4)classSlideSpec(BaseModel):
title:str
chart_id: Optional[str]=None
bullets: List[SlideBullet]= Field(min_length=2, max_length=4)classArtifactPlan(BaseModel):
charts: List[ChartSpec]= Field(min_length=3, max_length=6)
slides: List[SlideSpec]= Field(min_length=4, max_length=10)संचालित द्वारा
ऊपर दिया गया कोड स्निपेट Pydantic मॉडल्स का उपयोग करके प्लान परिभाषित करता है, जो तीन महत्वपूर्ण काम करते हैं:
प्लान संरचना:ChartKind = Literal["line", "bar"] चार्ट प्रकारों को एक छोटे enum तक सीमित करता है, ChartSpec पूर्ण चार्ट कॉन्ट्रैक्ट को कैप्चर करता है, और SlideSpec स्लाइड लेआउट को परिभाषित करता है, जबकि वैकल्पिक रूप से chart_id के जरिए किसी स्लाइड को चार्ट से जोड़ता है।
स्वचालित गुणवत्ता प्रवर्तन:Field() जैसी बाधाएँ सुनिश्चित करती हैं कि हमें हमेशा पर्याप्त चार्ट्स/स्लाइड्स मिलें, हर स्लाइड में पर्याप्त बुलेट्स हों और हर बुलेट में citations शामिल हों।
प्लानिंग और रेंडरिंग: GPT-5.2 केवल ब्लूप्रिंट—यानी चार्ट्स, स्लाइड्स और citations—structured JSON के रूप में बनाता है, जबकि बाकी कोड निष्पादन, Excel फ़ॉर्मेटिंग, चार्ट रेंडरिंग और डेक लेआउट संभालता है।
इस स्कीमा के साथ, GPT-5.2 एक ऐसा प्लानर बन जाता है जो validated JSON ब्लूप्रिंट तैयार करता है।
चरण 3: Structured outputs के साथ GPT-5.2 को कॉल करें
प्लान स्कीमा परिभाषित होने के बाद, अगला कदम है GPT-5.2 को “स्कीमा मोड” में कॉल करना—ताकि वह ऐसा JSON ब्लूप्रिंट लौटाए जिसे आप वैलिडेट कर सकें और निष्पादित कर सकें। मॉडल के “सही व्यवहार” की उम्मीद करने की बजाय, हम API को निर्देश देते हैं कि वह सीधे आपके JSON Schema में डिकोड करे, response_format: { type: "json_schema", ... strict: true } का उपयोग करके—Structured Outputs का उद्देश्य यही है।
chat_json_schema() फ़ंक्शन एकमात्र gateway है जो prompts को validated प्लान में बदलता है:
प्रोवाइडर रूटिंग (OpenRouter बनाम OpenAI): यदि मॉडल नाम नेमस्पेस्ड है (openai/gpt-5.2), तो आप डिफ़ॉल्ट रूप से OpenRouter लेते हैं क्योंकि OpenRouter मॉडल रूटिंग के लिए यही फ़ॉर्मेट उपयोग करता है। यदि आप स्पष्ट रूप से api_provider="openai" सेट करते हैं, तो वही Chat Completions payload OpenAI के endpoint पर भेजते हैं। चाहें तो OpenRouter को छोड़ दें।
एंडपॉइंट सेट करना: OpenAI के लिए, आप https://api.openai.com/v1/chat/completions endpoint को standard Bearer auth के साथ कॉल करते हैं। OpenRouter के लिए, आप https://openrouter.ai/api/v1/chat/completions endpoint को कॉल करते हैं और वैकल्पिक रूप से HTTP-Referer और X-Title जैसे attribution headers शामिल करते हैं, जिन्हें OpenRouter अनुशंसित metadata के रूप में दस्तावेज़ करता है।
response_format के जरिए स्कीमा डिकोडिंग: मुख्य payload ब्लॉक है response_format: { type: "json_schema", json_schema: { strict: true, schema: ... } }, जो मॉडल को हमारे JSON Schema तक आउटपुट सीमित करने का निर्देश देता है।
अगले चरण में, हम मॉडल के JSON प्लान को Pydantic से वैलिडेट करेंगे, और केवल तभी Excel चार्ट्स रेंडर और PowerPoint डेक जनरेट करेंगे जब प्लान bounds checks पास करेगा।
चरण 4: हेल्पर फ़ंक्शंस
किसी भी चीज़ को रेंडर करने से पहले, हमें एक छोटा लेयर चाहिए जो अपलोड किए गए CSV को विश्लेषण के लिए सुरक्षित और रेफरेंस के लिए सुसंगत बनाता है। ये हेल्पर फ़ंक्शंस चार काम करते हैं: CSV लोड और क्लीन करना, एक derived मॉडल टेबल बनाना, प्रॉम्प्ट के लिए संक्षिप्त facts निकालना, और Excel आउटपुट को autosized कॉलम्स के साथ पठनीय बनाना।
चरण 4.1: CSV लोड और वैलिडेट करें
यदि इनपुट टेबल खाली हो या न्यूमेरिक फ़ील्ड्स स्ट्रिंग्स के रूप में स्टोर हों, तो GPT 5.2 विश्वसनीय रूप से चार्ट्स और स्लाइड्स का प्लान नहीं बना सकता। यह फ़ंक्शन डेटासेट को मानकीकृत करता है ताकि downstream चरणों को अनुमान न लगाना पड़े।
defload_and_validate_csv(csv_path:str)-> pd.DataFrame:
df = pd.read_csv(csv_path)iflen(df)==0:raise ValueError("CSV is empty")iflen(df.columns)==0:raise ValueError("CSV has no columns")
date_cols =[c for c in df.columns ifany(x in c.lower()for x in['date','time','month','year','period','week'])]if date_cols:
date_col = date_cols[0]
df[date_col]= df[date_col].astype(str)for col in df.columns:if col notin date_cols:try:
df[col]= pd.to_numeric(df[col])if df[col].dtype in['float64','int64']:
df[col]= df[col].fillna(0)except(ValueError, TypeError):passreturn dfसंचालित द्वारा
यह फ़ंक्शन:
फ़ाइल को pd.read_csv. से DataFrame में पढ़ता है।
नाम-आधारित heuristics (date, month, week, period) से कॉलम्स डिटेक्ट करता है और उन्हें स्ट्रिंग में फोर्स करता है। इससे चार्ट और स्लाइड टेक्स्ट में लेबल स्थिर रहते हैं।
अंत में, non-date कॉलम्स को pd.to_numeric से न्यूमेरिक में कन्वर्ट करता है और भविष्य में NaN फैलने से बचने के लिए मिसिंग न्यूमेरिक वैल्यूज़ को शून्य से भरता है।
इस चरण के अंत में, आपके पास एक non-empty DataFrame होगा जिसमें न्यूमेरिक कॉलम्स मौजूद हैं।
चरण 4.2: मॉडल DataFrame की गणना
रॉ CSVs डोमेन के हिसाब से अस्त-व्यस्त और असंगत होते हैं। मॉडल शीट एक normalization लेयर की तरह काम करती है, जहाँ हम charting के लिए उपयोगी derived metrics जोड़ते हैं, बिना यूज़र को उन्हें मैन्युअली निकालने की ज़रूरत पड़े।
defcompute_model_df(raw_df: pd.DataFrame)-> pd.DataFrame:
df = raw_df.copy()
numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()if"new_mrr"in df.columns and"churned_mrr"in df.columns:
df["net_new_mrr"]= df["new_mrr"]- df["churned_mrr"]
revenue_cols =[c for c in df.columns ifany(x in c.lower()for x in['revenue','mrr','income','sales'])]
cost_cols =[c for c in df.columns ifany(x in c.lower()for x in['cost','cogs','expense'])]if revenue_cols and cost_cols:
revenue = df[revenue_cols[0]]
cost = df[cost_cols[0]]
df["gross_margin"]=((revenue - cost)/ revenue.replace(0, pd.NA)).fillna(0)for col in numeric_cols[:5]:if col notin df.columns:continue
df[f"{col}_change"]= df[col].pct_change().fillna(0)return dfसंचालित द्वारा
compute_model_df() फ़ंक्शन दो व्यापक रूप से उपयोगी कार्य करता है:
यह एक प्राथमिक आउटपुट वैल्यू कॉलम और एक सेकेंडरी इनपुट कॉलम ढूंढता है और एक सरल अनुपात निकालता है—जो बताता है कि सेकेंडरी सिग्नल का हिसाब करने के बाद प्राथमिक सिग्नल कितना बचता है।
कुछ न्यूमेरिक कॉलम्स के लिए, यह pct_change() निकालता है—जो डिफ़ॉल्ट रूप से पिछले रो से फ्रैक्शनल बदलाव देता है—और किसी भी टाइम-सीरीज़ मेट्रिक में मूवमेंट कैप्चर करने के लिए एक आसान बेसलाइन बन जाता है।
चरण 4.3: facts की गणना
Structured Outputs के बावजूद, हम GPT-5.2 को उस अवधि में क्या बदला उसका एक संक्षिप्त सारांश देना चाहेंगे। ये facts स्लाइड बुलेट्स में मॉडल के पुन: उपयोग के लिए प्रॉम्प्ट की तरह भी कार्य करते हैं।
defcompute_facts(model_df: pd.DataFrame)-> Dict[str,str]:defdelta(a:float, b:float)->float:return0.0if a ==0else(b - a)/ a
iflen(model_df)==0:return{"error":"No data"}
first = model_df.iloc[0]
last = model_df.iloc[-1]
date_cols =[c for c in model_df.columns ifany(x in c.lower()for x in['date','time','month','year','period','week'])]
period_col = date_cols[0]if date_cols else model_df.columns[0]
facts ={"period":f"{first[period_col]} → {last[period_col]}","row_count":len(model_df),"column_count":len(model_df.columns),}
numeric_cols = model_df.select_dtypes(include=[np.number]).columns.tolist()for i, col inenumerate(numeric_cols[:5]):if col in first.index and col in last.index:
start_val = safe_float(first[col])
end_val = safe_float(last[col])
avg_val = safe_float(model_df[col].mean())
facts[f"{col}_start"]= fmt_money(start_val)if start_val >=100elsef"{start_val:.2f}"
facts[f"{col}_end"]= fmt_money(end_val)if end_val >=100elsef"{end_val:.2f}"
facts[f"{col}_change_pct"]= fmt_pct(delta(start_val, end_val))
facts[f"{col}_avg"]= fmt_money(avg_val)if avg_val >=100elsef"{avg_val:.2f}"return factsसंचालित द्वारा
हम सबसे पहले पहली और आखिरी पंक्ति चुनते हैं ताकि अवधि विंडो परिभाषित हो। फिर हम मानवीय-पठनीय रिपोर्टिंग के लिए (date, month, week, period) में से कोई कॉलम चुनते हैं।
कुछ न्यूमेरिक कॉलम्स के लिए, हम start, end, average और start-to-end कुल प्रतिशत बदलाव का स्नैपशॉट निकालते हैं। इससे सिग्नल्स की एक हल्की-फुल्की dictionary बनती है जो मॉडल को स्लाइड बुलेट्स लिखने में मदद करती है।
चरण 4.4: Excel कॉलम्स को ऑटो-साइज़ करें
डिफ़ॉल्ट Excel कॉलम चौड़ाइयाँ जनरेटेड वर्कबुक्स को रफ दिखाती हैं। ऑटो-साइज़िंग एक सरल पॉलिश स्टेप है जो तुरंत उपयोगिता बढ़ा देता है।
defautosize_columns(ws, max_width:int=45):for col in ws.columns:
max_len =0
col_letter = get_column_letter(col[0].column)for cell in col:if cell.value isNone:continue
max_len =max(max_len,len(str(cell.value)))
ws.column_dimensions[col_letter].width =min(max_len +2, max_width)संचालित द्वारा
autosize_columns() फ़ंक्शन हर कॉलम स्कैन करता है, अधिकतम स्ट्रिंग लंबाई ढूंढता है और ws.column_dimensions[col_letter].width सेट करता है। OpenPyXL लाइब्रेरी column_dimensions और उसकी width प्रॉपर्टी को एक्सपोज़ करती है ताकि Excel में कॉलम डिस्प्ले साइज नियंत्रित किया जा सके।
अब हम क्लीन मॉडल टेबल और facts को GPT-5.2 प्लानिंग प्रॉम्प्ट में प्लग करेंगे, लौटे प्लान को वैलिडेट करेंगे, और फिर चार्ट्स व स्लाइड्स रेंडर करेंगे।
चरण 5: वर्कबुक बनाएं
पिछले चरण से क्लीन मॉडल टेबल और हल्के सारांश facts का उपयोग करते हुए, अब हम openpyxl के साथ एक नियतात्मक Excel वर्कबुक जनरेट करते हैं।
defbuild_workbook_base(raw_df: pd.DataFrame, model_df: pd.DataFrame, out_xlsx:str)-> Tuple[str,int]:
wb = Workbook()
wb.remove(wb.active)
header_fill = PatternFill("solid", fgColor="F2F2F2")
header_font = Font(bold=True)
ws_raw = wb.create_sheet("Raw")for j, col inenumerate(raw_df.columns, start=1):
c = ws_raw.cell(row=1, column=j, value=col)
c.font = header_font
c.fill = header_fill
c.alignment = Alignment(horizontal="center", vertical="center")for i, row inenumerate(raw_df.itertuples(index=False), start=2):for j, val inenumerate(row, start=1):
ws_raw.cell(row=i, column=j, value=val)
ws_raw.freeze_panes ="A2"
autosize_columns(ws_raw)
row_end =1+len(raw_df)
ws_model = wb.create_sheet("Model")for j, col inenumerate(model_df.columns, start=1):
c = ws_model.cell(row=1, column=j, value=col)
c.font = header_font
c.fill = header_fill
c.alignment = Alignment(horizontal="center", vertical="center")for i, row inenumerate(model_df.itertuples(index=False), start=2):for j, val inenumerate(row, start=1):
ws_model.cell(row=i, column=j, value=val)
ws_model.freeze_panes ="A2"
autosize_columns(ws_model)
col_index ={name: idx +1for idx, name inenumerate(model_df.columns)}
money_keywords =['mrr','revenue','income','sales','cost','expense','cash','balance','burn','price','amount','fee']
pct_keywords =['margin','rate','ratio','pct','percent','change','growth']for col_name, col_idx in col_index.items():
col_lower = col_name.lower()
is_money =any(kw in col_lower for kw in money_keywords)
is_pct =any(kw in col_lower for kw in pct_keywords)or'margin'in col_lower
is_large_number =Falseif col_name in model_df.columns:
sample_vals = model_df[col_name].dropna()iflen(sample_vals)>0and pd.api.types.is_numeric_dtype(sample_vals):
max_val =abs(sample_vals.max())
is_large_number = max_val >1000for r inrange(2, row_end +1):if is_money or is_large_number:
ws_model.cell(row=r, column=col_idx).number_format ="$#,##0"elif is_pct:
ws_model.cell(row=r, column=col_idx).number_format ="0.0%"elif pd.api.types.is_numeric_dtype(model_df[col_name]):
ws_model.cell(row=r, column=col_idx).number_format ="0.0"
ws_charts = wb.create_sheet("Charts")
ws_charts["A1"]="Charts"
ws_charts["A1"].font = Font(bold=True)
wb.save(out_xlsx)return out_xlsx, row_endसंचालित द्वारा
यह वर्कबुक बिल्डर यह करता है:
वर्कबुक ढाँचा बनाएँ: Workbook() एक नया वर्कबुक ऑब्जेक्ट बनाता है, फिर wb.remove(wb.active) डिफ़ॉल्ट शीट हटा देता है ताकि हम शीट क्रम को पूरी तरह नियंत्रित कर सकें। नई वर्कशीट्स स्पष्ट रूप से wb.create_sheet("Raw"), wb.create_sheet("Model"), और wb.create_sheet("Charts") से जोड़ी जाती हैं।
Raw शीट लिखें: हम Font, PatternFill और Alignment का उपयोग कर एक हेडर रो लिखते हैं, फिर raw_df की हर पंक्ति जोड़ते हैं। autosize_columns(ws_raw) फ़ंक्शन बिना मैन्युअल रिसाइज़िंग के पठनीयता में मदद करता है।
सीमाएँ निर्धारित करें: हम row_end = 1 + len(raw_df) लेते हैं, जो हमें एक स्थिर आखिरी रो देता है ताकि हम चार्ट श्रेणियों और citations के लिए A2:A{row_end} जैसी रेंजेस सुरक्षित रूप से बना सकें।
नंबर-फ़ॉर्मेट ह्यूरिस्टिक्स लागू करें: इसके बाद,हम cell.number_format को कॉलम-नाम कीवर्ड्स और dtype के आधार पर सेट करते हैं ताकि Excel वैल्यूज़ को मुद्रा, प्रतिशत या सामान्य नंबर के रूप में दिखाए।
Charts शीट बनाएं: wb.create_sheet("Charts") हमें चार्ट एंकर्स के लिए एक स्थिर कैनवास देता है। यह openpyxl चार्ट वर्कफ़्लो से मेल खाता है जहाँ हम LineChart या BarChart बनाते हैं, Reference के जरिए रेंजेस बाइंड करते हैं, set_categories से केटेगरी सेट करते हैं और फिर ws.add_chart() से उसे प्लेस करते हैं।
इस चरण के बाद, आपके पास एक सुसंगत Excel फ़ाइल है जिसे अगला चरण सही cell ranges द्वारा सुरक्षित रूप से रेफरेंस कर सकता है। चरण 6: चार्ट्स रेंडर करें
अब जब वर्कबुक संरचना फिक्स हो गई है, हम Charts शीट में चार्ट्स रेंडर कर सकते हैं और एक सुसंगत ब्रांड लुक लागू कर सकते हैं। मैंने DataCamp का कलर पैलेट लगाया है:
प्राइमरी रंग: #01ef63
सेकेंडरी रंग: #203147
defadd_charts_to_workbook(xlsx_path:str, charts: List[ChartSpec], metric_to_range: Dict[str,str]):
wb = load_workbook(xlsx_path)
ws_model = wb["Model"]
ws_charts = wb["Charts"]
header_map ={ws_model.cell(1, c).value: c for c inrange(1, ws_model.max_column +1)}defref_from_metric(metric:str, row_start:int, row_end:int)-> Reference:if metric notin header_map:raise ValueError(f"Unknown metric in Model sheet: {metric}")
col = header_map[metric]return Reference(ws_model, min_col=col, min_row=row_start, max_col=col, max_row=row_end)for ch in charts:
chart = LineChart()if ch.kind =="line"else BarChart()
chart.title = ch.title
x_metric = ch.x_metric
if x_metric notin header_map:
date_keywords =['date','time','month','year','period','week']
date_cols =[col for col in header_map.keys()if col andany(kw instr(col).lower()for kw in date_keywords)]if date_cols:
x_metric = date_cols[0]else:
x_metric =list(header_map.keys())[0]if header_map else"month"
x_ref = ref_from_metric(x_metric,2, ws_model.max_row)
chart.set_categories(x_ref)
chart_colors =["01EF63","203147"]for i, s inenumerate(ch.series):
y_ref = ref_from_metric(s.metric,2, ws_model.max_row)
chart.add_data(y_ref, titles_from_data=False)
chart.series[-1].title = SeriesLabel(v=s.name)
series = chart.series[-1]
color_hex = chart_colors[i %len(chart_colors)]try:if ch.kind =="line":
series.graphicalProperties.line.solidFill = color_hex
series.graphicalProperties.line.width =30000else:
series.graphicalProperties.solidFill = color_hex
except AttributeError:pass
anchor = ch.anchor.strip()if"!"in anchor:
anchor = anchor.split("!")[-1]if anchor and anchor.isalpha():
anchor =f"{anchor}2"ifnot re.match(r'^[A-Z]+[0-9]+संचालित द्वारा
add_charts_to_workbook() फ़ंक्शन हमारा चार्ट-रेंडरिंग इंजन है जो स्कीमा प्लान को ब्रांडेड Excel विजुअल्स में बदलता है।
वर्कबुक लोड करें: हम load_workbook(xlsx_path) से जनरेटेड Excel फ़ाइल खोलते हैं। इसके बाद पहले रो से एक header_map बनाते हैं जो हर कॉलम नाम को उसके Excel कॉलम इंडेक्स से मैप करता है।
सेल-रेंज रेफरेंस बनाएं: Reference() फ़ंक्शन प्लॉट की सटीक रेंज तय करता है, और chart.set_categories(x_ref) X-axis केटेगरीज़ को वायर करता है। स्टैंडर्ड पैटर्न है—रेफरेंस ऑब्जेक्ट बनाना, डेटा जोड़ना, फिर केटेगरी सेट करना।
चार्ट टाइप इंस्टेंशिएट करें: हर चार्ट स्पेक के लिए, हम या तो LineChart() या BarChart() बनाते हैं और chart.title सेट करते हैं।
ब्रांड पैलेट जोड़ें: स्पेक में हर सीरीज़ के लिए, हम chart.add_data() से Y-रेंज बाइंड करते हैं, फिर SeriesLabel() से डिस्प्ले लेबल सेट करते हैं।
अंततः, ws_charts.add_chart() फ़ंक्शन हर चार्ट को एक विशिष्ट टॉप-लेफ्ट सेल पर रखता है।
चरण 7: PowerPoint रेंडर करें
यहीं प्लान एक वास्तविक स्लाइड डेक बनता है। हम एक PowerPoint बनाते हैं, टाइटल्स और बुलेट्स ले-आउट करते हैं, उसी टेबल से बने चार्ट्स embed करते हैं जिसका उपयोग Excel के लिए किया गया था, और एक फ़ूटर जोड़ते हैं जो प्लान की cell-range citations को संरक्षित करता है।
defbuild_pptx(out_pptx:str, model_df: pd.DataFrame, plan: ArtifactPlan):
prs = Presentation()
blank = prs.slide_layouts[6]
chart_by_id ={c.id: c for c in plan.charts}defadd_title(slide, title:str):
box = slide.shapes.add_textbox(Inches(0.5), Inches(0.2), Inches(9.0), Inches(0.6))
tf = box.text_frame
tf.clear()
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(32)
p.font.bold =Truedefadd_bullets(slide, bullets: List[SlideBullet], left, top, width, height):
box = slide.shapes.add_textbox(left, top, width, height)
tf = box.text_frame
tf.word_wrap =True
tf.clear()for i, b inenumerate(bullets):
p = tf.paragraphs[0]if i ==0else tf.add_paragraph()
p.text = b.text
p.font.size = Pt(18)
p.level =0
p.space_before = Pt(4)
p.space_after = Pt(8)defadd_sources_footer(slide, bullets: List[SlideBullet]):
sources =[]
seen =set()for b in bullets:for c in b.citations:if c notin seen:
seen.add(c)
sources.append(c)ifnot sources:return
text ="Sources: "+"; ".join(sources)
box = slide.shapes.add_textbox(Inches(0.5), Inches(6.8), Inches(0.4))
tf = box.text_frame
tf.clear()
p = tf.paragraphs[0]
p.text = text
p.font.size = Pt(9)
p.font.italic =Truedefadd_chart(slide, chart_spec: ChartSpec, left, top, width, height):
date_cols =[c for c in model_df.columns ifany(x in c.lower()for x in['date','time','month','year','period','week'])]
x_col = date_cols[0]if date_cols else model_df.columns[0]
categories = model_df[x_col].astype(str).tolist()
chart_data = CategoryChartData()
chart_data.categories = categories
chart_colors =["01EF63","203147"]for i, s inenumerate(chart_spec.series):
values = model_df[s.metric].apply(safe_float).tolist()
chart_data.add_series(s.name, values)
chart_type = XL_CHART_TYPE.LINE if chart_spec.kind =="line"else XL_CHART_TYPE.COLUMN_CLUSTERED
graphic_frame = slide.shapes.add_chart(chart_type, left, top, width, height, chart_data)
chart = graphic_frame.chart
chart.has_title =True
chart.chart_title.text_frame.text = chart_spec.title
chart.has_legend =True
chart.legend.position = XL_LEGEND_POSITION.BOTTOM
chart.legend.include_in_layout =Falsefrom pptx.dml.color import RGBColor
for i, series inenumerate(chart.series):
color_hex = chart_colors[i %len(chart_colors)]
color_rgb = RGBColor(int(color_hex[0:2],16),int(color_hex[2:4],16),int(color_hex[4:6],16))
fill = series.format.fill
fill.solid()
fill.fore_color.rgb = color_rgb
if chart_spec.kind =="line":
line = series.format.line
line.color.rgb = color_rgb
line.width = Pt(3)for s in plan.slides:
slide = prs.slides.add_slide(blank)
add_title(slide, s.title)
has_chart =bool(s.chart_id and s.chart_id in chart_by_id)if has_chart:
add_bullets(
slide,
s.bullets,
left=Inches(0.5),
top=Inches(1.0),
width=Inches(4.5),
height=Inches(5.5),)
add_chart(
slide,
chart_by_id[s.chart_id],
left=Inches(5.2),
top=Inches(1.0),
width=Inches(4.5),
height=Inches(5.5),)else:
add_bullets(
slide,
s.bullets,
left=Inches(0.5),
top=Inches(1.0),
width=Inches(9.0),
height=Inches(5.5),)
add_sources_footer(slide, s.bullets)
prs.save(out_pptx)defbuild_allowed_ranges(model_df: pd.DataFrame, row_end:int)-> Dict[str,str]:
ranges ={}for i, col inenumerate(model_df.columns, start=1):
col_letter = get_column_letter(i)
ranges[col]=f"Model!{col_letter}2:{col_letter}{row_end}"return ranges
defvalidate_plan(plan: ArtifactPlan, allowed_ranges: Dict[str,str])->None:
allowed_set =set(allowed_ranges.values())
chart_ids ={c.idfor c in plan.charts}iflen(chart_ids)!=len(plan.charts):raise ValueError("Duplicate chart ids in plan.")for s in plan.slides:if s.chart_id and s.chart_id notin chart_ids:raise ValueError(f"Slide references unknown chart_id: {s.chart_id}")for s in plan.slides:for b in s.bullets:for c in b.citations:if c notin allowed_set:raise ValueError(f"Citation not in allowed set: {c}")
allowed_metrics =set(allowed_ranges.keys())for c in plan.charts:if c.x_metric notin allowed_metrics:passfor s in c.series:if s.metric notin allowed_metrics:raise ValueError(f"Unknown series metric: {s.metric}")संचालित द्वारा
आइए ऊपर के कोड को चरण-दर-चरण समझें:
डेक बनाएँ और चार्ट्स इंडेक्स करें: prs = Presentation() एक नया PPTX शुरू करता है, जबकि blank = prs.slide_layouts[6] हमें सटीक प्लेसमेंट के लिए खाली कैनवास देता है।
टाइटल्स और बुलेट्स: add_title() एक टेक्स्टबॉक्स के text_frame में लिखता है, और add_bullets() हर बुलेट के लिए एक पैराग्राफ जोड़ता है—फ़ॉन्ट साइज और स्पेसिंग को पैराग्राफ फ़ॉर्मैटिंग से नियंत्रित करता है।
साइटेशंस फ़ूटर: add_sources_footer() बुलेट्स में citations को deduplicate करता है और नीचे एक कॉम्पैक्ट Sources: लाइन रेंडर करता है, ताकि ट्रेसएबिलिटी बनी रहे और स्लाइड भरी-भरी न लगे।
चार्ट embed करें: add_chart() time-जैसे X कॉलम और planned सीरीज़ वैल्यूज़ का उपयोग करते हुए CategoryChartData() बनाता है, फिर slide.shapes.add_chart() के जरिए insert करता है, जो एक GraphicFrame लौटाता है।
ब्रांड स्टाइलिंग: अंत में, हम hex को RGBColor में बदलकर प्रति सीरीज़ लागू करते हैं।
लेआउट नियम और गार्डरेल्स: यदि किसी स्लाइड में वैध chart_id है तो हम दो-कॉलम लेआउट लेते हैं, अन्यथा फुल-विथ बुलेट्स। build_allowed_ranges() citable रेंजेस की allow-list बनाता है और validate_plan() यूनिक चार्ट IDs, वैध रेफरेंसेज़, स्वीकृत citations और वैध metrics लागू करता है ताकि मॉडल फ़ील्ड्स न गढ़ सके।
चरण 8: LLM से प्लान जनरेट करें
अब हमारे पास साफ टेबुलर डेटा है, जिसे हम एक स्कीमा बिल्ड प्लान (चार्ट्स, स्लाइड्स और citations) में बदलेंगे जिसे रेंडरर निष्पादित कर सके।
defgenerate_plan_with_llm(
api_key:str,
model:str,
goal:str,
allowed_ranges: Dict[str,str],
facts: Dict[str,str],
num_slides:int=6,
num_charts:int=4,)-> ArtifactPlan:
plan_schema ={"type":"object","properties":{"charts":{"type":"array","minItems":3,"maxItems":6,"items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["line","bar"]},"title":{"type":"string"},"x_metric":{"type":"string"},"series":{"type":"array","minItems":1,"maxItems":3,"items":{"type":"object","properties":{"name":{"type":"string"},"metric":{"type":"string"},},"required":["name","metric"],"additionalProperties":False,},},"anchor":{"type":"string"},},"required":["id","kind","title","x_metric","series","anchor"],"additionalProperties":False,},},"slides":{"type":"array","minItems":4,"maxItems":10,"items":{"type":"object","properties":{"title":{"type":"string"},"chart_id":{"type":["string","null"]},"bullets":{"type":"array","minItems":2,"maxItems":4,"items":{"type":"object","properties":{"text":{"type":"string"},"citations":{"type":"array","minItems":1,"maxItems":4,"items":{"type":"string"},},},"required":["text","citations"],"additionalProperties":False,},},},"required":["title","chart_id","bullets"],"additionalProperties":False,},},},"required":["charts","slides"],"additionalProperties":False,}
system =("You produce investor-grade slides with traceability.\n""Output MUST match the JSON schema exactly.\n""Rules:\n""1) Use ONLY the allowed citation ranges (exact strings).\n""2) No generic filler. Every bullet must contain at least ONE number (e.g., $X, X%, X mo).\n""3) Keep bullets short: <= 12 words.\n""4) Prefer trends (start→end) or averages; avoid speculation.\n""5) Provide exactly the requested number of charts and slides.\n""6) Use chart_id to attach the correct chart to each slide.\n""7) Chart anchors: Use cell references ONLY (e.g., 'A2', 'A20', 'A38'). Do NOT include sheet name like 'CHARTS!A'.\n")
user =(f"Goal: {goal}\n"f"Requested: {num_charts} charts and {num_slides} slides.\n\n"f"Facts (use these; do not invent):\n{json.dumps(facts, indent=2)}\n\n"f"Allowed citation ranges (use exact values only):\n{json.dumps(list(allowed_ranges.values()), indent=2)}\n\n""Metric keys available for charts:\n"f"{json.dumps(list(allowed_ranges.keys()), indent=2)}\n\n""Chart requirements:\n""- x_metric: Use a date/time column (e.g., 'month', 'date', 'period', 'week') for the X-axis.\n"" If no date column exists, use the first column from the available metrics.\n""- Create meaningful charts based on available metrics in the data.\n""- Focus on trends, comparisons, and key business metrics.\n""- Chart colors: Use these specific shades for all chart series:\n"" * Primary color: #01ef63 (bright green)\n"" * Secondary color: #203147 (dark blue/navy)\n"" * Alternate between these two colors for multiple series in the same chart.\n""Slide requirements:\n""- Create slides that tell a story with the available data.\n""- Suggested structure: Overview, Key Metrics, Trends, Analysis, Summary, Next Steps.\n""- Adapt slide titles and content to match the data domain (e.g., sales, marketing, operations, finance).\n")
messages =[{"role":"system","content": system},{"role":"user","content": user}]
plan_json = chat_json_schema(
model=model,
messages=messages,
json_schema=plan_schema,
api_key=api_key,
temperature=0.2,
max_tokens=6000,)try:
plan = ArtifactPlan.model_validate(plan_json)
validate_plan(plan, allowed_ranges)iflen(plan.slides)!= num_slides:raise ValueError(f"Expected {num_slides} slides, got {len(plan.slides)}")iflen(plan.charts)!= num_charts:raise ValueError(f"Expected {num_charts} charts, got {len(plan.charts)}")return plan
except(ValidationError, ValueError)as e:
repair = messages +[{"role":"assistant","content": json.dumps(plan_json)},{"role":"user","content":f"Fix JSON to satisfy schema + rules. Error:\n{str(e)}"},]
plan_json = chat_json_schema(
model=model,
messages=repair,
json_schema=plan_schema,
api_key=api_key,
temperature=0.0,
max_tokens=6000,)
plan = ArtifactPlan.model_validate(plan_json)
validate_plan(plan, allowed_ranges)iflen(plan.slides)!= num_slides orlen(plan.charts)!= num_charts:raise RuntimeError("Plan repaired but counts still wrong. Tighten prompt or increase max_tokens.")return planसंचालित द्वारा
generate_plan_with_llm() फ़ंक्शन इस डेमो का मस्तिष्क है। इसे पाइपलाइन में ऐसे जोड़ते हैं:
स्पष्ट JSON Schema: plan_schema उस एकमात्र रिस्पॉन्स शेप को परिभाषित करता है जिसे मॉडल बना सकता है—charts[] और slides[]—आवश्यक फ़ील्ड्स और enums के साथ।
सिस्टम प्रॉम्प्ट गार्डरेल्स: हम non-negotiables हार्ड-कोड करते हैं ताकि प्लान renderable और auditable रहे। हम चार्ट प्लेसमेंट को plain cell तक सीमित करते हैं ताकि लेआउट नियतात्मक रहे—मॉडल-इंप्रोवाइज़्ड नहीं। ऐसे मल्टी-स्टेप नियंत्रणों को संभालने के लिए ही GPT-5.2 डिज़ाइन किया गया है।
यूज़र मैसेज: यूज़र मैसेज लक्ष्य, अनुरोधित काउंट्स, computed facts और उपलब्ध मेट्रिक keys देता है। चूंकि मॉडल कुछ और नहीं देखता, इसलिए उसके पास कॉलम्स, रेंजेस या unsupported दावों को गढ़ने के कम मौके रहते हैं।
स्कीमा-अनुरूप डिकोडिंग: हम chat_json_schema() को response_formatjson_schema और strict: true के साथ कॉल करते हैं, जो डिकोडिंग के दौरान स्कीमा अनुपालन लागू करता है।
दो परतों में वैलिडेशन: पहले ArtifactPlan.model_validate(plan_json) यह कन्फर्म करता है कि रिस्पॉन्स हमारे Pydantic मॉडल्स से मेल खाता है। फिर validate_plan() डोमेन कन्स्ट्रेंट्स लागू करता है।
चरण 9: पाइपलाइन चलाएँ
अंततः, हम CSV लोडिंग, Excel जनरेशन, LLM प्लानिंग, चार्ट रेंडरिंग और अंतिम PPTX एक्सपोर्ट को एक साथ वायर करते हैं, फिर आउटपुट पाथ्स लौटाते हैं।
आइए पाइपलाइन के सभी कॉम्पोनेंट्स को चरण-दर-चरण जोड़ें:
Structured पाइपलाइन आउटपुट्स: @dataclassxlsx_path, pptx_path, plan_path और logs के लिए एक हल्का कंटेनर बनाता है, ताकि Streamlit UI पाइपलाइन परिणाम को ट्यूपल्स या ग्लोबल्स को संभालने की बजाय एक सिंगल structured वैल्यू की तरह ट्रीट कर सके।
API key रूटिंग: load_dotenv() हमारी .env फ़ाइल को environment variables में लोड करता है ताकि OPENAI_API_KEY और OPENROUTER_API_KEY को os.getenv() से पढ़ा जा सके। फिर हम एक सरल नियम से प्रोवाइडर key चुनते हैं—यदि OPENROUTER_API_KEY मौजूद है तो OpenRouter, अन्यथा उपलब्ध होने पर OpenAI।
डेटा प्रेप चलाएँ: हम CSV लोड और वैलिडेट करते हैं, derived मॉडल टेबल निकालते हैं, और build_workbook_base() से Excel स्केलेटन बनाते हैं। generate_plan_with_llm() फिर एक ArtifactPlan लौटाता है जिसे json.dump() से JSON के रूप में सेव किया जाता है ताकि उसे आसानी से inspect और debug किया जा सके।
आर्टिफैक्ट्स रेंडर करें: add_charts_to_workbook() फ़ंक्शन Excel में Charts शीट पर चार्ट्स को materialize करता है, और build_pptx() प्लान और टेबल से स्लाइड डेक बनाता है।
इस चरण के बाद, हमारा Streamlit ऐप run_pipeline() कॉल कर सकता है, logs दिखा सकता है और तीन नियतात्मक आउटपुट डाउनलोड के लिए दे सकता है—Excel वर्कबुक, PPTX डेक और JSON प्लान।
चरण 10: Streamlit UI
अब जब कोर पाइपलाइन तैयार है, अंतिम चरण इसे एक Streamlit ऐप में लपेटना है ताकि कोई भी फ़ाइल अपलोड कर सके, लक्ष्य सेट कर सके, मॉडल चुन सके और जनरेट किए गए आर्टिफैक्ट्स डाउनलोड कर सके।
load_dotenv()
st.set_page_config(
page_title="GPT-5.2 PPT and Excel Generator",
page_icon="",
layout="wide",
initial_sidebar_state="expanded")
st.markdown("""
<style>
.main-header {
font-size: 2.5rem;
font-weight: 700;
color: #1f77b4;
margin-bottom: 1rem;
}
.sub-header {
font-size: 1.2rem;
color: #666;
margin-bottom: 2rem;
}
.success-box {
padding: 1rem;
background-color: #d4edda;
border-left: 4px solid #28a745;
margin: 1rem 0;
}
.error-box {
padding: 1rem;
background-color: #f8d7da;
border-left: 4px solid #dc3545;
margin: 1rem 0;
}
.info-box {
padding: 1rem;
background-color: #d1ecf1;
border-left: 4px solid #17a2b8;
margin: 1rem 0;
}
.slide-preview {
border: 2px solid #ddd;
border-radius: 8px;
padding: 10px;
margin: 10px 0;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
""", unsafe_allow_html=True)defmain():try:if'outputs'notin st.session_state:
st.session_state.outputs =Noneif'generated'notin st.session_state:
st.session_state.generated =False
st.markdown('<div class="main-header">Spreadsheet to PowerPoint Generator</div>', unsafe_allow_html=True)with st.sidebar:# API Provider selection
api_provider = st.selectbox("API Provider",["OpenAI","OpenRouter"],help="Select which API to use. Make sure the corresponding API key is set in your .env file")# Model selection based on providerif api_provider =="OpenAI":
model = st.selectbox("AI Model",["gpt-4o","gpt-4-turbo","gpt-4","gpt-3.5-turbo",],help="OpenAI models. Requires OPENAI_API_KEY in .env file")else:# OpenRouter
model = st.selectbox("AI Model",["openai/gpt-5.2","openai/gpt-4-turbo","openai/gpt-4o","anthropic/claude-3-opus","anthropic/claude-3-sonnet",],help="OpenRouter models. Requires OPENROUTER_API_KEY in .env file")with st.expander("Advanced Settings"):
num_slides = st.slider("Number of Slides",4,10,6)
num_charts = st.slider("Number of Charts",3,6,4)
col1, col2 = st.columns([1,1])with col1:
st.markdown("### Input")
uploaded_file = st.file_uploader("Upload CSV",type=['csv'],help="Upload any CSV file. The app will automatically detect columns and create appropriate charts and slides.")
use_example = st.checkbox("Use example data (kpis.csv)", value=Trueifnot uploaded_file elseFalse)if uploaded_file or use_example:if use_example:
csv_path ="kpis.csv"
df = pd.read_csv(csv_path)else:
df = pd.read_csv(uploaded_file)with tempfile.NamedTemporaryFile(delete=False, suffix='.csv', mode='w')as f:
df.to_csv(f, index=False)
csv_path = f.name
with st.expander(" Preview Data", expanded=False):try:
st.dataframe(df, width='stretch')except Exception as e:
st.write("**Data Preview:**")
st.table(df.head(20))iflen(df)>20:
st.caption(f"Showing first 20 of {len(df)} rows")
st.caption(f"Rows: {len(df)} | Columns: {len(df.columns)}")else:
csv_path =None
st.info("Upload a CSV or use example data to continue")
goal = st.text_area("Goal",
value="Create a 6-slide investor update for Apr–Sep 2025. Focus on growth, retention, unit economics, and runway.",
height=100,help="Describe what you want the AI to create")
generate_btn = st.button("Generate Artifacts",type="primary", use_container_width=True)with col2:
st.markdown("### Output")
status_container = st.empty()
progress_bar = st.progress(0)ifnot generate_btn:
status_container.info("Configure settings and click 'Generate Artifacts'")if generate_btn:ifnot csv_path:
st.error("Please upload a CSV or select 'Use example data'")
st.stop()try:
status_container.info("Processing... This may take 30-60 seconds")
progress_bar.progress(10)
output_dir = Path("out")
output_dir.mkdir(exist_ok=True)
progress_bar.progress(20)with st.spinner("Generating artifacts..."):
outputs: RunOutputs = run_pipeline(
csv_path=csv_path,
goal=goal,
outdir=str(output_dir),
model=model,
num_slides=num_slides,
num_charts=num_charts
)
st.session_state.outputs = outputs
st.session_state.generated =True
progress_bar.progress(100)except Exception as e:
status_container.markdown(f'<div class="error-box"> <b>Error:</b> {str(e)}</div>',
unsafe_allow_html=True)
st.exception(e)
st.session_state.generated =Falseif st.session_state.generated and st.session_state.outputs:
outputs = st.session_state.outputs
try:if"Error"notin outputs.logs:
status_container.markdown('<div class="success-box"><b>Success!</b> Artifacts generated successfully</div>',
unsafe_allow_html=True)
tab1, tab2, tab3, tab4 = st.tabs(["PowerPoint","Excel","JSON Plan","Logs"])with tab1:
st.markdown("### PowerPoint Slides")
pptx_path = Path(outputs.pptx_path)if pptx_path.exists():try:
prs = Presentation(str(pptx_path))
slide_count =len(prs.slides)except:
slide_count ="unknown"withopen(pptx_path,"rb")as f:
st.download_button(
label="Download PowerPoint",
data=f.read(),
file_name="investor_update.pptx",
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation",
width='stretch',
key="pptx_download")
st.markdown("---")
col_info1, col_info2 = st.columns(2)with col_info1:
st.metric("Slides", slide_count)with col_info2:
file_size = pptx_path.stat().st_size /1024# KB
st.metric("File Size",f"{file_size:.1f} KB")else:
st.error("PowerPoint file not found")with tab2:
st.markdown("### Excel Workbook")
xlsx_path = Path(outputs.xlsx_path)if xlsx_path.exists():withopen(xlsx_path,"rb")as f:
st.download_button(
label="Download Excel",
data=f.read(),
file_name="investor_update.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
width='stretch',
key="xlsx_download")
wb = openpyxl.load_workbook(xlsx_path)
st.markdown("#### Data Preview")
ws = wb["Model"]
data =[]
headers =[cell.value for cell in ws[1]]for row in ws.iter_rows(min_row=2, values_only=True):
data.append(row)
df_preview = pd.DataFrame(data, columns=headers)try:
st.dataframe(df_preview.head(10), width='stretch')except Exception:
st.table(df_preview.head(10))iflen(df_preview)>10:
st.caption(f"Showing first 10 of {len[df_preview]} rows")else:
st.error("Excel file not found")with tab3:
st.markdown("### Schema-Locked JSON Plan")
plan_path = Path(outputs.plan_path)if plan_path.exists():withopen(plan_path,"r")as f:
plan_data = json.load(f)
st.download_button(
label="Download JSON",
data=json.dumps(plan_data, indent=2),
file_name="plan.json",
mime="application/json",
key="json_download")
st.markdown("---")
st.json(plan_data)
col_a, col_b = st.columns(2)with col_a:
st.metric("Charts Generated",len(plan_data.get("charts",[])))with col_b:
st.metric("Slides Generated",len(plan_data.get("slides",[])))else:
st.error("JSON plan not found")with tab4:
st.markdown("### Generation Logs")
st.code(outputs.logs, language="text")else:
status_container.markdown(f'<div class="error-box"> <b>Error occurred</b><br>{outputs.logs}</div>',
unsafe_allow_html=True)except Exception as e:
st.error(f"Error displaying results: {str(e)}")
st.exception(e)except Exception as e:
st.error(f"Fatal Error: {str(e)}")
st.exception(e)
st.info("Try installing dependencies: pip install -r requirements.txt")if __name__ =="__main__":try:
main()except Exception as e:import sys
print(f"Fatal error: {e}",file=sys.stderr)import traceback
traceback.print_exc()संचालित द्वारा
Streamlit लेयर में, हम st.set_page_config( layout="wide") से ऐप फ्रेम सेट करते हैं, फिर st.columns() से स्क्रीन को विभाजित करते हैं ताकि बायाँ भाग इनपुट ले जबकि दायाँ भाग रन कंट्रोल्स और आउटपुट दिखाए। पाइपलाइन तभी चलती है जब यूज़र st.button() क्लिक करता है—जहाँ हम पहले मिसिंग अपलोड्स के खिलाफ गार्ड करते हैं, st.spinner() लपेटते हैं ताकि UI फ्रीज़ न लगे, फिर हम st.download_button() के जरिए Excel, PowerPoint और प्लान फ़ाइल यूज़र को वापस देते हैं।
यह चरण पूरा होने के बाद, हम सब कुछ app.py के रूप में सेव करते हैं और पूरे अनुभव को इस कमांड से लॉन्च करते हैं:
इस ट्यूटोरियल में, हमने एक पूर्ण “CSV to artifacts” पाइपलाइन बनाई जो GPT-5.2 का उपयोग करके चार्ट्स, स्लाइड स्ट्रक्चर और cell-range citations सहित एक प्लान लौटाती है। फिर हम कुछ भी जनरेट करने से पहले उस प्लान को वैलिडेट करते हैं। यह plan-then-render दृष्टिकोण स्प्रेडशीट्स और प्रेजेंटेशंस जैसे मल्टी-स्टेप वर्कफ़्लोज़ पर GPT-5.2 की ताकतों से अच्छी तरह मेल खाता है।
नतीजा एक दोहराए जा सकने वाला वर्कफ़्लो है—यूज़र CSV अपलोड करता है, कोड एक साफ वर्कबुक फाउंडेशन बनाता है, GPT-5.2 ऐसा प्लान तैयार करता है जो केवल allow-listed Excel रेंजेस को ही cite कर सकता है, और रेंडरर openpyxl तथा python-pptx का उपयोग करते हुए चार्ट्स और स्लाइड्स materialize करता है।
यहाँ से, आप बिना कोर लॉजिक बदले डेमो को बढ़ा सकते हैं—जैसे और चार्ट प्रकार जोड़ना, स्लाइड टेम्पलेट्स लाना, multi-CSV जोइन्स सपोर्ट करना आदि। मुख्य पैटर्न वही रहता है—मॉडल को एक ऐसे प्लानर की तरह ट्रीट करें जिसे स्कीमा का पालन करना ही होगा, और सारी रेंडरिंग लॉजिक कोड में रखें ताकि आउटपुट सुसंगत और debuggable रहें।
मैं एमएल (Gen AI) में Google Developers Expert, Kaggle 3x Expert, और Women Techmakers एंबेसडर हूँ, तथा तकनीक क्षेत्र में 3+ वर्षों का अनुभव रखती हूँ। मैंने 2020 में एक हेल्थ-टेक स्टार्टअप की सह-स्थापना की और वर्तमान में Georgia Tech से कंप्यूटर विज्ञान में मास्टर कर रही हूँ, जिसमें मेरा विशेषज्ञता क्षेत्र मशीन लर्निंग है।
FAQs
GPT-5.2 को Streamlit के साथ क्यों जोड़ें?⌄
Streamlit एक AI वर्कफ़्लो को बिना पूरा फ्रंटएंड बनाए इंटरैक्टिव एप्लिकेशन में बदलना आसान बनाता है। जब इसे GPT-5.2 के साथ जोड़ा जाता है, तो डेवलपर्स प्रयोग से एक ऐसे उपयोगी टूल तक तेजी से जा सकते हैं जो इनपुट लेता है, मॉडल-चालित प्रक्रिया चलाता है और ठोस आउटपुट वापस देता है।
Structured Outputs क्या हैं और यहाँ क्यों महत्वपूर्ण हैं?⌄
Structured Outputs मॉडल को बाध्य करते हैं कि वह पूर्व-परिभाषित JSON Schema के अनुरूप ही डेटा लौटाए। इससे चार्ट्स, स्लाइड्स, मेट्रिक्स और citations सख्त नियमों का पालन करते हैं, और किसी भी Excel या PowerPoint फ़ाइल के बनने से पहले प्लान को वैलिडेट करना संभव हो जाता है।
क्या ऐसे ऐप्स बनाने के लिए मुझे गहरे फ्रंटएंड अनुभव की ज़रूरत है?⌄
नहीं। Streamlit को इस तरह डिज़ाइन किया गया है कि अधिकांश काम Python में, परिचित कंट्रोल फ्लो और लाइब्रेरीज़ का उपयोग करते हुए होता है। इससे उन डेटा साइंटिस्ट्स और एनालिस्ट्स के लिए बाधाएँ कम होती हैं जो जावा स्क्रिप्ट या जटिल वेब फ्रेमवर्क सीखे बिना AI-पावर्ड टूल्स डिप्लॉय करना चाहते हैं।
GPT-5.2 एक मजबूत मॉडल है जो ऐसे वर्कफ़्लो और मल्टी-स्टेप पाइपलाइनों को जनरेट करने में सक्षम है जिन्हें विश्वसनीयता, संरचना और कम hallucinations की जरूरत होती है। OpenAI ने long-context कार्य और स्प्रेडशीट-स्टाइल टास्क पर बेहतर परफॉर्मेंस को हाईलाइट किया है।
इस ट्यूटोरियल में, हम एक Streamlit ऐप बनाएंगे जो एक जूनियर एनालिस्ट की तरह काम करता है:
मुख्य बात यह है कि GPT-5.2 सीधे “स्लाइड्स नहीं बनाता।” यह एक सख्त प्लान बनाता है जिसे आपका कोड नियतात्मक रूप से निष्पादित करता है।
साथ ही OpenAI के नए मॉडलों पर हमारे गाइड भी पढ़ें: GPT-5.5, GPT-5.4, GPT-5.3 Codex और GPT-5.3 Instant
GPT-5.2 क्या है?
GPT-5.2 OpenAI का नवीनतम GPT-5-सीरीज़ मॉडल है, जिसमें स्प्रेडशीट बनाना, प्रेजेंटेशन तैयार करना, कोड लिखना और डीबग करना, लंबे कॉन्टेक्स्ट को समझना और टूल्स का उपयोग करना जैसे end-to-end कार्यों के लिए सुधार शामिल हैं।
इस प्रोजेक्ट के लिए GPT-5.2 की प्रमुख विशेषताएँ:
GPT-5.2 उदाहरण प्रोजेक्ट: Structured Artifacts Generator बनाएं
इस सेक्शन में, हम Streamlit ऐप में रैप किए गए GPT 5.2 मॉडल का उपयोग करके एक structured artifacts जनरेटर (Excel और PowerPoint) बनाएंगे। उच्च स्तर पर, अंतिम Streamlit ऐप यह करता है:
यह CSV पढ़ता है और एक canonical वर्कबुक बनाता है
फिर, यह Structured Outputs (
response_format: json_schema) के साथ OpenAI या OpenRouter के जरिए GPT-5.2 को कॉल करता हैवापस आए प्लान को हमारे स्कीमा के अनुसार वैलिडेट करता है
Excel में चार्ट्स रेंडर करता है और उन्हें हमारे ब्रांड पैलेट के अनुरूप स्टाइल करता है
अंत में, citations सहित embedded चार्ट्स के साथ एक PowerPoint जनरेट करता है
इसे चरण-दर-चरण बनाते हैं।
चरण 1: पूर्वापेक्षाएँ और सेटअप
GPT-5.2 के साथ Excel वर्कबुक और PowerPoint डेक जनरेट करने से पहले, हमें एक लोकल एनवायरनमेंट सेट करना होगा। हम कोर लाइब्रेरीज़ इंस्टॉल करने और API keys को environment variables के रूप में सेट करने से शुरू करेंगे।
हम Streamlit का उपयोग इंटरैक्टिव वेब UI बनाने के लिए करेंगे, Pandas से CSV लोड और तैयार करेंगे, Pydantic से GPT-5.2 के schema-locked JSON प्लान को वैलिडेट करेंगे। हम python-dotenv का उपयोग
.envफ़ाइल सेOPENROUTER_API_KEY/OPENAI_API_KEYजैसी keys को सुरक्षित रूप से लोड करने के लिए भी करेंगे। requests लाइब्रेरी OpenRouter/OpenAI-समर्थित endpoint को कॉल करती है, जबकि openpyxl Excel वर्कबुक और चार्ट्स जनरेट करता है, और python-pptx native (editable) चार्ट्स के साथ PowerPoint डेक बनाता है।अगला, हम अपनी API key/s सेट करते हैं:
ध्यान दें कि GPT 5.2 को कई सेवाओं के माध्यम से एक्सेस किया जा सकता है, जिसमें OpenAI का आधिकारिक API तथा OpenRouter जैसी सेवाएँ शामिल हैं।
API keys को लोकली स्टोर करने के लिए
.envफ़ाइल का उपयोग करने की सिफारिश की जाती है:.envफ़ाइल में निम्नलिखित सेव करें:इसके बाद, इन API keys को load_dotenv लाइब्रेरी का उपयोग करके लोड करें:
इस बिंदु पर, हमारा एनवायरनमेंट OpenAI API के साथ प्रमाणित होने के लिए तैयार है।
चरण 2: प्लान स्कीमा परिभाषित करना
अब, हम एक schema-locked artifact प्लान परिभाषित करते हैं जो एक सख्त JSON ब्लूप्रिंट है—यह GPT-5.2 को बताता है कि कौन से चार्ट बनाने हैं और स्लाइड्स को कैसे संरचित करना है, ताकि मॉडल का आउटपुट मुक्त-रूप टेक्स्ट के बजाय
response_format: { type: "json_schema", strict: true }के जरिए Structured Outputs का उपयोग करके डिकोड और वैलिडेट किया जा सके।ऊपर दिया गया कोड स्निपेट Pydantic मॉडल्स का उपयोग करके प्लान परिभाषित करता है, जो तीन महत्वपूर्ण काम करते हैं:
प्लान संरचना:
ChartKind = Literal["line", "bar"]चार्ट प्रकारों को एक छोटे enum तक सीमित करता है,ChartSpecपूर्ण चार्ट कॉन्ट्रैक्ट को कैप्चर करता है, औरSlideSpecस्लाइड लेआउट को परिभाषित करता है, जबकि वैकल्पिक रूप सेchart_idके जरिए किसी स्लाइड को चार्ट से जोड़ता है।स्वचालित गुणवत्ता प्रवर्तन:
Field()जैसी बाधाएँ सुनिश्चित करती हैं कि हमें हमेशा पर्याप्त चार्ट्स/स्लाइड्स मिलें, हर स्लाइड में पर्याप्त बुलेट्स हों और हर बुलेट में citations शामिल हों।प्लानिंग और रेंडरिंग: GPT-5.2 केवल ब्लूप्रिंट—यानी चार्ट्स, स्लाइड्स और citations—structured JSON के रूप में बनाता है, जबकि बाकी कोड निष्पादन, Excel फ़ॉर्मेटिंग, चार्ट रेंडरिंग और डेक लेआउट संभालता है।
इस स्कीमा के साथ, GPT-5.2 एक ऐसा प्लानर बन जाता है जो validated JSON ब्लूप्रिंट तैयार करता है।
चरण 3: Structured outputs के साथ GPT-5.2 को कॉल करें
प्लान स्कीमा परिभाषित होने के बाद, अगला कदम है GPT-5.2 को “स्कीमा मोड” में कॉल करना—ताकि वह ऐसा JSON ब्लूप्रिंट लौटाए जिसे आप वैलिडेट कर सकें और निष्पादित कर सकें। मॉडल के “सही व्यवहार” की उम्मीद करने की बजाय, हम API को निर्देश देते हैं कि वह सीधे आपके JSON Schema में डिकोड करे,
response_format: { type: "json_schema", ... strict: true }का उपयोग करके—Structured Outputs का उद्देश्य यही है।chat_json_schema()फ़ंक्शन एकमात्र gateway है जो prompts को validated प्लान में बदलता है:प्रोवाइडर रूटिंग (OpenRouter बनाम OpenAI): यदि मॉडल नाम नेमस्पेस्ड है (openai/gpt-5.2), तो आप डिफ़ॉल्ट रूप से OpenRouter लेते हैं क्योंकि OpenRouter मॉडल रूटिंग के लिए यही फ़ॉर्मेट उपयोग करता है। यदि आप स्पष्ट रूप से api_provider="openai" सेट करते हैं, तो वही Chat Completions payload OpenAI के endpoint पर भेजते हैं। चाहें तो OpenRouter को छोड़ दें।
एंडपॉइंट सेट करना: OpenAI के लिए, आप
https://api.openai.com/v1/chat/completionsendpoint को standard Bearer auth के साथ कॉल करते हैं। OpenRouter के लिए, आपhttps://openrouter.ai/api/v1/chat/completionsendpoint को कॉल करते हैं और वैकल्पिक रूप से HTTP-Referer और X-Title जैसे attribution headers शामिल करते हैं, जिन्हें OpenRouter अनुशंसित metadata के रूप में दस्तावेज़ करता है।response_formatके जरिए स्कीमा डिकोडिंग: मुख्य payload ब्लॉक हैresponse_format: { type: "json_schema", json_schema: { strict: true, schema: ... } }, जो मॉडल को हमारे JSON Schema तक आउटपुट सीमित करने का निर्देश देता है।अगले चरण में, हम मॉडल के JSON प्लान को Pydantic से वैलिडेट करेंगे, और केवल तभी Excel चार्ट्स रेंडर और PowerPoint डेक जनरेट करेंगे जब प्लान bounds checks पास करेगा।
चरण 4: हेल्पर फ़ंक्शंस
किसी भी चीज़ को रेंडर करने से पहले, हमें एक छोटा लेयर चाहिए जो अपलोड किए गए CSV को विश्लेषण के लिए सुरक्षित और रेफरेंस के लिए सुसंगत बनाता है। ये हेल्पर फ़ंक्शंस चार काम करते हैं: CSV लोड और क्लीन करना, एक derived मॉडल टेबल बनाना, प्रॉम्प्ट के लिए संक्षिप्त facts निकालना, और Excel आउटपुट को autosized कॉलम्स के साथ पठनीय बनाना।
चरण 4.1: CSV लोड और वैलिडेट करें
यदि इनपुट टेबल खाली हो या न्यूमेरिक फ़ील्ड्स स्ट्रिंग्स के रूप में स्टोर हों, तो GPT 5.2 विश्वसनीय रूप से चार्ट्स और स्लाइड्स का प्लान नहीं बना सकता। यह फ़ंक्शन डेटासेट को मानकीकृत करता है ताकि downstream चरणों को अनुमान न लगाना पड़े।
यह फ़ंक्शन:
pd.read_csv.से DataFrame में पढ़ता है।pd.to_numericसे न्यूमेरिक में कन्वर्ट करता है और भविष्य में NaN फैलने से बचने के लिए मिसिंग न्यूमेरिक वैल्यूज़ को शून्य से भरता है।इस चरण के अंत में, आपके पास एक non-empty DataFrame होगा जिसमें न्यूमेरिक कॉलम्स मौजूद हैं।
चरण 4.2: मॉडल DataFrame की गणना
रॉ CSVs डोमेन के हिसाब से अस्त-व्यस्त और असंगत होते हैं। मॉडल शीट एक normalization लेयर की तरह काम करती है, जहाँ हम charting के लिए उपयोगी derived metrics जोड़ते हैं, बिना यूज़र को उन्हें मैन्युअली निकालने की ज़रूरत पड़े।
compute_model_df()फ़ंक्शन दो व्यापक रूप से उपयोगी कार्य करता है:यह एक प्राथमिक आउटपुट वैल्यू कॉलम और एक सेकेंडरी इनपुट कॉलम ढूंढता है और एक सरल अनुपात निकालता है—जो बताता है कि सेकेंडरी सिग्नल का हिसाब करने के बाद प्राथमिक सिग्नल कितना बचता है।
कुछ न्यूमेरिक कॉलम्स के लिए, यह
pct_change()निकालता है—जो डिफ़ॉल्ट रूप से पिछले रो से फ्रैक्शनल बदलाव देता है—और किसी भी टाइम-सीरीज़ मेट्रिक में मूवमेंट कैप्चर करने के लिए एक आसान बेसलाइन बन जाता है।चरण 4.3: facts की गणना
Structured Outputs के बावजूद, हम GPT-5.2 को उस अवधि में क्या बदला उसका एक संक्षिप्त सारांश देना चाहेंगे। ये facts स्लाइड बुलेट्स में मॉडल के पुन: उपयोग के लिए प्रॉम्प्ट की तरह भी कार्य करते हैं।
हम सबसे पहले पहली और आखिरी पंक्ति चुनते हैं ताकि अवधि विंडो परिभाषित हो। फिर हम मानवीय-पठनीय रिपोर्टिंग के लिए (date, month, week, period) में से कोई कॉलम चुनते हैं।
कुछ न्यूमेरिक कॉलम्स के लिए, हम start, end, average और start-to-end कुल प्रतिशत बदलाव का स्नैपशॉट निकालते हैं। इससे सिग्नल्स की एक हल्की-फुल्की dictionary बनती है जो मॉडल को स्लाइड बुलेट्स लिखने में मदद करती है।
चरण 4.4: Excel कॉलम्स को ऑटो-साइज़ करें
डिफ़ॉल्ट Excel कॉलम चौड़ाइयाँ जनरेटेड वर्कबुक्स को रफ दिखाती हैं। ऑटो-साइज़िंग एक सरल पॉलिश स्टेप है जो तुरंत उपयोगिता बढ़ा देता है।
autosize_columns()फ़ंक्शन हर कॉलम स्कैन करता है, अधिकतम स्ट्रिंग लंबाई ढूंढता है औरws.column_dimensions[col_letter].widthसेट करता है। OpenPyXL लाइब्रेरीcolumn_dimensionsऔर उसकीwidthप्रॉपर्टी को एक्सपोज़ करती है ताकि Excel में कॉलम डिस्प्ले साइज नियंत्रित किया जा सके।अब हम क्लीन मॉडल टेबल और facts को GPT-5.2 प्लानिंग प्रॉम्प्ट में प्लग करेंगे, लौटे प्लान को वैलिडेट करेंगे, और फिर चार्ट्स व स्लाइड्स रेंडर करेंगे।
चरण 5: वर्कबुक बनाएं
पिछले चरण से क्लीन मॉडल टेबल और हल्के सारांश facts का उपयोग करते हुए, अब हम openpyxl के साथ एक नियतात्मक Excel वर्कबुक जनरेट करते हैं।
यह वर्कबुक बिल्डर यह करता है:
वर्कबुक ढाँचा बनाएँ:
Workbook()एक नया वर्कबुक ऑब्जेक्ट बनाता है, फिरwb.remove(wb.active)डिफ़ॉल्ट शीट हटा देता है ताकि हम शीट क्रम को पूरी तरह नियंत्रित कर सकें। नई वर्कशीट्स स्पष्ट रूप सेwb.create_sheet("Raw"),wb.create_sheet("Model"), औरwb.create_sheet("Charts")से जोड़ी जाती हैं।Raw शीट लिखें: हम Font, PatternFill और Alignment का उपयोग कर एक हेडर रो लिखते हैं, फिर raw_df की हर पंक्ति जोड़ते हैं।
autosize_columns(ws_raw)फ़ंक्शन बिना मैन्युअल रिसाइज़िंग के पठनीयता में मदद करता है।सीमाएँ निर्धारित करें: हम
row_end = 1 + len(raw_df)लेते हैं, जो हमें एक स्थिर आखिरी रो देता है ताकि हम चार्ट श्रेणियों और citations के लिएA2:A{row_end}जैसी रेंजेस सुरक्षित रूप से बना सकें।नंबर-फ़ॉर्मेट ह्यूरिस्टिक्स लागू करें: इसके बाद, हम
cell.number_formatको कॉलम-नाम कीवर्ड्स और dtype के आधार पर सेट करते हैं ताकि Excel वैल्यूज़ को मुद्रा, प्रतिशत या सामान्य नंबर के रूप में दिखाए।Charts शीट बनाएं:
wb.create_sheet("Charts")हमें चार्ट एंकर्स के लिए एक स्थिर कैनवास देता है। यह openpyxl चार्ट वर्कफ़्लो से मेल खाता है जहाँ हम LineChart या BarChart बनाते हैं,Referenceके जरिए रेंजेस बाइंड करते हैं,set_categoriesसे केटेगरी सेट करते हैं और फिरws.add_chart()से उसे प्लेस करते हैं।इस चरण के बाद, आपके पास एक सुसंगत Excel फ़ाइल है जिसे अगला चरण सही cell ranges द्वारा सुरक्षित रूप से रेफरेंस कर सकता है।
चरण 6: चार्ट्स रेंडर करें
अब जब वर्कबुक संरचना फिक्स हो गई है, हम Charts शीट में चार्ट्स रेंडर कर सकते हैं और एक सुसंगत ब्रांड लुक लागू कर सकते हैं। मैंने DataCamp का कलर पैलेट लगाया है:
add_charts_to_workbook()फ़ंक्शन हमारा चार्ट-रेंडरिंग इंजन है जो स्कीमा प्लान को ब्रांडेड Excel विजुअल्स में बदलता है।वर्कबुक लोड करें: हम
load_workbook(xlsx_path)से जनरेटेड Excel फ़ाइल खोलते हैं। इसके बाद पहले रो से एकheader_mapबनाते हैं जो हर कॉलम नाम को उसके Excel कॉलम इंडेक्स से मैप करता है।सेल-रेंज रेफरेंस बनाएं:
Reference()फ़ंक्शन प्लॉट की सटीक रेंज तय करता है, औरchart.set_categories(x_ref)X-axis केटेगरीज़ को वायर करता है। स्टैंडर्ड पैटर्न है—रेफरेंस ऑब्जेक्ट बनाना, डेटा जोड़ना, फिर केटेगरी सेट करना।चार्ट टाइप इंस्टेंशिएट करें: हर चार्ट स्पेक के लिए, हम या तो
LineChart()याBarChart()बनाते हैं औरchart.titleसेट करते हैं।ब्रांड पैलेट जोड़ें: स्पेक में हर सीरीज़ के लिए, हम
chart.add_data()से Y-रेंज बाइंड करते हैं, फिरSeriesLabel()से डिस्प्ले लेबल सेट करते हैं।अंततः,
ws_charts.add_chart()फ़ंक्शन हर चार्ट को एक विशिष्ट टॉप-लेफ्ट सेल पर रखता है।चरण 7: PowerPoint रेंडर करें
यहीं प्लान एक वास्तविक स्लाइड डेक बनता है। हम एक PowerPoint बनाते हैं, टाइटल्स और बुलेट्स ले-आउट करते हैं, उसी टेबल से बने चार्ट्स embed करते हैं जिसका उपयोग Excel के लिए किया गया था, और एक फ़ूटर जोड़ते हैं जो प्लान की cell-range citations को संरक्षित करता है।
आइए ऊपर के कोड को चरण-दर-चरण समझें:
डेक बनाएँ और चार्ट्स इंडेक्स करें:
prs = Presentation()एक नया PPTX शुरू करता है, जबकिblank = prs.slide_layouts[6]हमें सटीक प्लेसमेंट के लिए खाली कैनवास देता है।टाइटल्स और बुलेट्स:
add_title()एक टेक्स्टबॉक्स केtext_frameमें लिखता है, औरadd_bullets()हर बुलेट के लिए एक पैराग्राफ जोड़ता है—फ़ॉन्ट साइज और स्पेसिंग को पैराग्राफ फ़ॉर्मैटिंग से नियंत्रित करता है।साइटेशंस फ़ूटर:
add_sources_footer()बुलेट्स में citations को deduplicate करता है और नीचे एक कॉम्पैक्टSources:लाइन रेंडर करता है, ताकि ट्रेसएबिलिटी बनी रहे और स्लाइड भरी-भरी न लगे।चार्ट embed करें:
add_chart()time-जैसे X कॉलम और planned सीरीज़ वैल्यूज़ का उपयोग करते हुएCategoryChartData()बनाता है, फिरslide.shapes.add_chart()के जरिए insert करता है, जो एकGraphicFrameलौटाता है।ब्रांड स्टाइलिंग: अंत में, हम hex को
RGBColorमें बदलकर प्रति सीरीज़ लागू करते हैं।लेआउट नियम और गार्डरेल्स: यदि किसी स्लाइड में वैध
chart_idहै तो हम दो-कॉलम लेआउट लेते हैं, अन्यथा फुल-विथ बुलेट्स।build_allowed_ranges()citable रेंजेस की allow-list बनाता है औरvalidate_plan()यूनिक चार्ट IDs, वैध रेफरेंसेज़, स्वीकृत citations और वैध metrics लागू करता है ताकि मॉडल फ़ील्ड्स न गढ़ सके।चरण 8: LLM से प्लान जनरेट करें
अब हमारे पास साफ टेबुलर डेटा है, जिसे हम एक स्कीमा बिल्ड प्लान (चार्ट्स, स्लाइड्स और citations) में बदलेंगे जिसे रेंडरर निष्पादित कर सके।
generate_plan_with_llm()फ़ंक्शन इस डेमो का मस्तिष्क है। इसे पाइपलाइन में ऐसे जोड़ते हैं:स्पष्ट JSON Schema:
plan_schemaउस एकमात्र रिस्पॉन्स शेप को परिभाषित करता है जिसे मॉडल बना सकता है—charts[]औरslides[]—आवश्यक फ़ील्ड्स और enums के साथ।सिस्टम प्रॉम्प्ट गार्डरेल्स: हम non-negotiables हार्ड-कोड करते हैं ताकि प्लान renderable और auditable रहे। हम चार्ट प्लेसमेंट को plain cell तक सीमित करते हैं ताकि लेआउट नियतात्मक रहे—मॉडल-इंप्रोवाइज़्ड नहीं। ऐसे मल्टी-स्टेप नियंत्रणों को संभालने के लिए ही GPT-5.2 डिज़ाइन किया गया है।
यूज़र मैसेज: यूज़र मैसेज लक्ष्य, अनुरोधित काउंट्स, computed facts और उपलब्ध मेट्रिक keys देता है। चूंकि मॉडल कुछ और नहीं देखता, इसलिए उसके पास कॉलम्स, रेंजेस या unsupported दावों को गढ़ने के कम मौके रहते हैं।
स्कीमा-अनुरूप डिकोडिंग: हम
chat_json_schema()कोresponse_formatjson_schemaऔरstrict: trueके साथ कॉल करते हैं, जो डिकोडिंग के दौरान स्कीमा अनुपालन लागू करता है।दो परतों में वैलिडेशन: पहले
ArtifactPlan.model_validate(plan_json)यह कन्फर्म करता है कि रिस्पॉन्स हमारे Pydantic मॉडल्स से मेल खाता है। फिरvalidate_plan()डोमेन कन्स्ट्रेंट्स लागू करता है।चरण 9: पाइपलाइन चलाएँ
अंततः, हम CSV लोडिंग, Excel जनरेशन, LLM प्लानिंग, चार्ट रेंडरिंग और अंतिम PPTX एक्सपोर्ट को एक साथ वायर करते हैं, फिर आउटपुट पाथ्स लौटाते हैं।
आइए पाइपलाइन के सभी कॉम्पोनेंट्स को चरण-दर-चरण जोड़ें:
@dataclassxlsx_path,pptx_path,plan_pathऔरlogsके लिए एक हल्का कंटेनर बनाता है, ताकि Streamlit UI पाइपलाइन परिणाम को ट्यूपल्स या ग्लोबल्स को संभालने की बजाय एक सिंगल structured वैल्यू की तरह ट्रीट कर सके।load_dotenv()हमारी.envफ़ाइल को environment variables में लोड करता है ताकिOPENAI_API_KEYऔरOPENROUTER_API_KEYकोos.getenv()से पढ़ा जा सके। फिर हम एक सरल नियम से प्रोवाइडर key चुनते हैं—यदिOPENROUTER_API_KEYमौजूद है तो OpenRouter, अन्यथा उपलब्ध होने पर OpenAI।build_workbook_base()से Excel स्केलेटन बनाते हैं।generate_plan_with_llm()फिर एकArtifactPlanलौटाता है जिसेjson.dump()से JSON के रूप में सेव किया जाता है ताकि उसे आसानी से inspect और debug किया जा सके।add_charts_to_workbook()फ़ंक्शन Excel में Charts शीट पर चार्ट्स को materialize करता है, औरbuild_pptx()प्लान और टेबल से स्लाइड डेक बनाता है।इस चरण के बाद, हमारा Streamlit ऐप
run_pipeline()कॉल कर सकता है,logsदिखा सकता है और तीन नियतात्मक आउटपुट डाउनलोड के लिए दे सकता है—Excel वर्कबुक, PPTX डेक और JSON प्लान।चरण 10: Streamlit UI
अब जब कोर पाइपलाइन तैयार है, अंतिम चरण इसे एक Streamlit ऐप में लपेटना है ताकि कोई भी फ़ाइल अपलोड कर सके, लक्ष्य सेट कर सके, मॉडल चुन सके और जनरेट किए गए आर्टिफैक्ट्स डाउनलोड कर सके।
Streamlit लेयर में, हम
st.set_page_config( layout="wide")से ऐप फ्रेम सेट करते हैं, फिरst.columns()से स्क्रीन को विभाजित करते हैं ताकि बायाँ भाग इनपुट ले जबकि दायाँ भाग रन कंट्रोल्स और आउटपुट दिखाए। पाइपलाइन तभी चलती है जब यूज़रst.button()क्लिक करता है—जहाँ हम पहले मिसिंग अपलोड्स के खिलाफ गार्ड करते हैं,st.spinner()लपेटते हैं ताकि UI फ्रीज़ न लगे, फिर हमst.download_button()के जरिए Excel, PowerPoint और प्लान फ़ाइल यूज़र को वापस देते हैं।यह चरण पूरा होने के बाद, हम सब कुछ
app.pyके रूप में सेव करते हैं और पूरे अनुभव को इस कमांड से लॉन्च करते हैं: