Rakshitjan commited on
Commit
a9f4704
Β·
verified Β·
1 Parent(s): d83092c

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile (1) +37 -0
  2. README.md +99 -10
  3. app (1).py +385 -0
  4. gitattributes +35 -0
  5. requirements (1).txt +6 -0
Dockerfile (1) ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FROM python:3.9-slim
2
+
3
+ # WORKDIR /app
4
+
5
+ # # Install system dependencies
6
+ # RUN apt-get update && apt-get install -y \
7
+ # build-essential \
8
+ # && rm -rf /var/lib/apt/lists/*
9
+
10
+ # # Copy and install requirements
11
+ # COPY requirements.txt .
12
+ # RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # # Copy application files
15
+ # COPY . .
16
+
17
+ # # Expose ports for both FastAPI and Gradio
18
+ # EXPOSE 7860
19
+ # EXPOSE 8000
20
+
21
+ # # Run the combined application
22
+ # CMD ["python", "app.py"]
23
+
24
+
25
+ FROM python:3.9
26
+
27
+ RUN useradd -m -u 1000 user
28
+ USER user
29
+ ENV PATH="/home/user/.local/bin:$PATH"
30
+
31
+ WORKDIR /app
32
+
33
+ COPY --chown=user ./requirements.txt requirements.txt
34
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
35
+
36
+ COPY --chown=user . /app
37
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,99 @@
1
- ---
2
- title: TestSeriesAITestScheduler
3
- emoji: πŸŒ–
4
- colorFrom: green
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Test Creation Agent
3
+ emoji: πŸ“
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ sdk_version: latest
8
+ app_port: 7860
9
+ pinned: false
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
+
14
+ # Test Creation Agent
15
+
16
+ This application provides a conversational interface to collect educational test creation parameters. It extracts details like chapters, question counts, difficulty distribution, and test timing from natural language inputs.
17
+
18
+ ## Features
19
+
20
+ - User-friendly Gradio chat interface
21
+ - Intelligent parameter extraction from natural language
22
+ - Supports multiple academic subjects and chapters
23
+ - Normalizes chapter names to standardized curriculum topics
24
+ - Tracks conversation state and guides users through completion
25
+
26
+ ## Architecture
27
+
28
+ The application consists of two main components:
29
+
30
+ 1. **FastAPI Backend**: Handles the parameter extraction and conversation logic
31
+ 2. **Gradio Frontend**: Provides the user interface for conversation
32
+
33
+ ## Setup
34
+
35
+ ### Environment Variables
36
+
37
+ Create a `.env` file from the example:
38
+
39
+ ```bash
40
+ cp .env.example .env
41
+ ```
42
+
43
+ Edit the `.env` file and add your OpenAI API key:
44
+
45
+ ```
46
+ OPENAI_API_KEY=your_openai_api_key_here
47
+ ```
48
+
49
+ ### Running with Docker
50
+
51
+ Build and run the Docker container:
52
+
53
+ ```bash
54
+ docker build -t test-creation-agent .
55
+ docker run -p 7860:7860 -p 8000:8000 --env-file .env test-creation-agent
56
+ ```
57
+
58
+ ### Running Locally
59
+
60
+ Install dependencies:
61
+
62
+ ```bash
63
+ pip install -r requirements.txt
64
+ ```
65
+
66
+ Run the application:
67
+
68
+ ```bash
69
+ python app.py
70
+ ```
71
+
72
+ This will start both the FastAPI backend and the Gradio frontend. Access the application at http://localhost:7860.
73
+
74
+ ## API Endpoints
75
+
76
+ The FastAPI backend provides these endpoints:
77
+
78
+ - `GET /`: Check if the API is running
79
+ - `POST /chat`: Send a user message and get a response
80
+ - Request body: `{"message": "string", "session_id": "string"}`
81
+ - `GET /session/{session_id}`: Get the current state of a session
82
+ - `DELETE /session/{session_id}`: Delete a session
83
+ - `POST /reset`: Reset a session to start over
84
+ - Request body: `{"message": "", "session_id": "string"}`
85
+
86
+ ## Deploying to Hugging Face Spaces
87
+
88
+ 1. Create a new Space on Hugging Face with Docker template
89
+ 2. Link your GitHub repository or upload the files directly
90
+ 3. Set the OPENAI_API_KEY in the Space's secrets
91
+ 4. The application will be accessible at your Space's URL
92
+
93
+ ## Usage Example
94
+
95
+ Simply open the Gradio interface and start describing your test requirements. For example:
96
+
97
+ "I need a test on Thermodynamics and Electrostatics, 10 questions each, 60% medium, 20% easy, 20% hard, 90 minutes, on May 15, 2025 at 10 AM"
98
+
99
+ The agent will extract the parameters, normalize chapter names, and either ask for missing information or confirm when all parameters are collected.
app (1).py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Request
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
+ from pydantic import BaseModel
5
+ import openai
6
+ import os
7
+ import json
8
+ import re
9
+ from typing import Dict, List, Optional, Tuple, Any
10
+
11
+ app = FastAPI(title="TestCreationAgent",
12
+ description="An API for collecting test creation parameters through conversation")
13
+
14
+ # Add CORS middleware to allow requests from frontend
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=["*"], # Allows all origins
18
+ allow_credentials=True,
19
+ allow_methods=["*"], # Allows all methods
20
+ allow_headers=["*"], # Allows all headers
21
+ )
22
+
23
+ # Define subject chapters mapping
24
+ SUBJECT_CHAPTERS = {
25
+ "Mathematics": [
26
+ "Number Systems", "Polynomials", "Coordinate Geometry", "Linear Equations in Two Variables",
27
+ "Introduction to Euclid's Geometry", "Lines and Angles", "Triangles", "Quadrilaterals",
28
+ "Areas of Parallelograms and Triangles", "Circles", "Constructions", "Heron's Formula",
29
+ "Surface Areas and Volumes", "Statistics", "Probability", "Real Numbers",
30
+ "Pair of Linear Equations in Two Variables", "Quadratic Equations", "Arithmetic Progressions",
31
+ "Introduction to Trigonometry", "Some Applications of Trigonometry", "Areas Related to Circles",
32
+ "Sets", "Relations and Functions", "Trigonometric Functions", "Principle of Mathematical Induction",
33
+ "Complex Numbers and Quadratic Equations", "Linear Inequalities", "Permutations and Combinations",
34
+ "Binomial Theorem", "Sequences and Series", "Straight Lines", "Conic Sections",
35
+ "Introduction to Three Dimensional Geometry", "Limits and Derivatives",
36
+ "Inverse Trigonometric Functions", "Matrices", "Determinants",
37
+ "Continuity and Differentiability", "Application of Derivatives", "Integrals",
38
+ "Application of Integrals", "Differential Equations", "Vector Algebra",
39
+ "Three Dimensional Geometry", "Linear Programming"
40
+ ],
41
+ "Physics": [
42
+ "Motion", "Force and Laws of Motion", "Gravitation", "Work and Energy", "Sound",
43
+ "Light: Reflection and Refraction", "Human Eye and Colourful World", "Electricity",
44
+ "Magnetic Effects of Electric Current", "Physical World and Measurement", "Kinematics",
45
+ "Laws of Motion", "Work, Energy and Power", "Motion of System of Particles and Rigid Body",
46
+ "Properties of Bulk Matter", "Thermodynamics", "Behaviour of Perfect Gases and Kinetic Theory",
47
+ "Oscillations and Waves", "Electrostatics", "Current Electricity",
48
+ "Magnetic Effects of Current and Magnetism", "Electromagnetic Induction and Alternating Currents",
49
+ "Electromagnetic Waves", "Optics", "Dual Nature of Radiation and Matter", "Atoms", "Nuclei",
50
+ "Semiconductor Electronics: Materials, Devices and Simple Circuits", "Vectors"
51
+ ],
52
+ "Chemistry": [
53
+ "Matter in Our Surroundings", "Is Matter Around Us Pure?", "Atoms and Molecules",
54
+ "Structure of the Atom", "Chemical Reactions and Equations", "Acids, Bases and Salts",
55
+ "Metals and Non-metals", "Carbon and Its Compounds", "Periodic Classification of Elements",
56
+ "Some Basic Concepts of Chemistry", "Structure of Atom",
57
+ "Classification of Elements and Periodicity in Properties",
58
+ "Chemical Bonding and Molecular Structure", "States of Matter: Gases and Liquids",
59
+ "Thermodynamics", "Equilibrium", "Redox Reactions",
60
+ "Organic Chemistry: Some Basic Principles and Techniques", "Hydrocarbons",
61
+ "Environmental Chemistry", "Solid State", "Solutions", "Electrochemistry",
62
+ "Chemical Kinetics", "Surface Chemistry", "General Principles and Processes of Isolation of Elements",
63
+ "p-Block Elements", "d- and f-Block Elements", "Coordination Compounds",
64
+ "Haloalkanes and Haloarenes", "Alcohols, Phenols and Ethers",
65
+ "Aldehydes, Ketones and Carboxylic Acids", "Amines", "Biomolecules", "Polymers",
66
+ "Chemistry in Everyday Life"
67
+ ],
68
+ "Organic Chemistry": [
69
+ "Organic Chemistry: Some Basic Principles and Techniques", "Hydrocarbons",
70
+ "Haloalkanes and Haloarenes", "Alcohols, Phenols and Ethers",
71
+ "Aldehydes, Ketones and Carboxylic Acids", "Amines", "Biomolecules",
72
+ "Polymers", "Chemistry in Everyday Life"
73
+ ],
74
+ "Inorganic Chemistry": [
75
+ "Classification of Elements and Periodicity in Properties",
76
+ "Chemical Bonding and Molecular Structure", "Redox Reactions",
77
+ "p-Block Elements", "d- and f-Block Elements", "Coordination Compounds"
78
+ ]
79
+ }
80
+
81
+ # Create a flat mapping of misspelled/approximate chapter names to correct ones
82
+ CHAPTER_MAPPING = {}
83
+ for subject, chapters in SUBJECT_CHAPTERS.items():
84
+ for chapter in chapters:
85
+ # Add the correct chapter name
86
+ CHAPTER_MAPPING[chapter.lower()] = (subject, chapter)
87
+
88
+ # Add common misspellings/variations
89
+ if chapter.lower() == "thermodynamics":
90
+ CHAPTER_MAPPING["termodyanamics"] = (subject, chapter)
91
+ CHAPTER_MAPPING["termodyn"] = (subject, chapter)
92
+ CHAPTER_MAPPING["thermo"] = (subject, chapter)
93
+ CHAPTER_MAPPING["thermodynamic"] = (subject, chapter)
94
+
95
+
96
+ class UserInput(BaseModel):
97
+ message: str
98
+ session_id: str
99
+
100
+
101
+ class SessionState(BaseModel):
102
+ params: Dict[str, str] = {
103
+ "chapters_of_the_test": "",
104
+ "questions_per_chapter": "",
105
+ "difficulty_distribution": "",
106
+ "test_duration": "",
107
+ "test_date": "",
108
+ "test_time": ""
109
+ }
110
+ completed: bool = False
111
+ attempt_count: int = 0
112
+
113
+
114
+ # In-memory session storage
115
+ sessions = {}
116
+
117
+
118
+ def normalize_chapter_name(chapter_input: str) -> Optional[Tuple[str, str]]:
119
+ """
120
+ Maps user input to standardized chapter names from the curriculum.
121
+ Returns tuple of (subject, correct_chapter_name) or None if no match.
122
+ """
123
+ if not chapter_input:
124
+ return None
125
+
126
+ # Direct mapping for exact matches or known misspellings
127
+ norm_input = chapter_input.lower().strip()
128
+ if norm_input in CHAPTER_MAPPING:
129
+ return CHAPTER_MAPPING[norm_input]
130
+
131
+ # Try fuzzy matching if no direct match
132
+ # Look for partial matches
133
+ for chapter_key, (subject, correct_name) in CHAPTER_MAPPING.items():
134
+ if norm_input in chapter_key or chapter_key in norm_input:
135
+ return (subject, correct_name)
136
+
137
+ # No match found
138
+ return None
139
+
140
+
141
+ async def llm_extractParams(user_input: str, current_params: Dict[str, str]) -> Dict[str, str]:
142
+ """
143
+ Extracts structured test parameters from natural language input
144
+ and updates the provided params dictionary.
145
+ """
146
+ system_prompt = """
147
+ You are an expert educational test creation assistant that extracts test setup parameters from user input.
148
+ Extract ONLY the parameters explicitly mentioned in the user's message.
149
+
150
+ Return a JSON object with all the following keys:
151
+ - chapters_of_the_test (string: list of chapters or topics)
152
+ - questions_per_chapter (string or number: how many questions per chapter)
153
+ - difficulty_distribution (string: e.g., "easy:40%, medium:40%, hard:20%" or any format specified)
154
+ - test_duration (string or number: time in minutes)
155
+ - test_date (string: in any reasonable date format)
156
+ - test_time (string: time of day)
157
+
158
+ Important rules:
159
+ - Do NOT make assumptions - if information isn't provided, leave as empty string ("")
160
+ - Only fill in values explicitly mentioned by the user
161
+ - For difficulty_distribution:
162
+ * Convert numeric sequences like "30 40 30" to "easy:30%, medium:40%, hard:30%" if they appear to be distributions
163
+ * Convert descriptions like "mostly hard" to approximate percentages (e.g., "easy:20%, medium:20%, hard:60%")
164
+ * Accept formats like "60 easy, 20 medium, 20 hard" and convert to percentages
165
+ - Return valid JSON with all keys, even if empty
166
+ """
167
+ messages = [
168
+ {"role": "system", "content": system_prompt},
169
+ {"role": "user", "content": user_input}
170
+ ]
171
+
172
+ try:
173
+ response = openai.chat.completions.create(
174
+ model="gpt-4o-mini",
175
+ messages=messages,
176
+ temperature=0.2
177
+ )
178
+
179
+ extracted_json = response.choices[0].message.content.strip()
180
+
181
+ # Handle potential JSON formatting issues by extracting JSON from response
182
+ if not extracted_json.startswith('{'):
183
+ # Find JSON object in text if it's not a clean JSON response
184
+ start_idx = extracted_json.find('{')
185
+ end_idx = extracted_json.rfind('}') + 1
186
+ if start_idx >= 0 and end_idx > start_idx:
187
+ extracted_json = extracted_json[start_idx:end_idx]
188
+ else:
189
+ raise ValueError("Unable to extract valid JSON from response")
190
+
191
+ # Parse and update the current_params safely
192
+ extracted_dict = json.loads(extracted_json)
193
+ updated_params = current_params.copy()
194
+
195
+ for key in updated_params:
196
+ if key.lower() in extracted_dict and extracted_dict[key.lower()]:
197
+ updated_params[key] = extracted_dict[key.lower()]
198
+ elif key in extracted_dict and extracted_dict[key]:
199
+ updated_params[key] = extracted_dict[key]
200
+
201
+ # Apply chapter mapping if chapters were specified
202
+ if updated_params["chapters_of_the_test"] and updated_params["chapters_of_the_test"] != current_params["chapters_of_the_test"]:
203
+ chapters_input = updated_params["chapters_of_the_test"]
204
+ # Split multiple chapters if comma-separated
205
+ chapter_list = [ch.strip() for ch in re.split(r',|;', chapters_input)]
206
+
207
+ mapped_chapters = []
208
+ for chapter in chapter_list:
209
+ result = normalize_chapter_name(chapter)
210
+ if result:
211
+ subject, correct_name = result
212
+ mapped_chapters.append(f"{correct_name} ({subject})")
213
+ else:
214
+ mapped_chapters.append(chapter) # Keep as-is if no mapping found
215
+
216
+ updated_params["chapters_of_the_test"] = ", ".join(mapped_chapters)
217
+
218
+ return updated_params
219
+
220
+ except json.JSONDecodeError as e:
221
+ print(f"Error: Could not parse response as JSON: {e}")
222
+ return current_params
223
+ except Exception as e:
224
+ print(f"Error during parameter extraction: {e}")
225
+ return current_params
226
+
227
+
228
+ def gate(params: Dict[str, str]) -> List[str]:
229
+ """
230
+ Checks which fields are still empty in the params.
231
+ Returns a list of missing parameter keys.
232
+ """
233
+ return [key for key, val in params.items() if not val]
234
+
235
+
236
+ async def llm_getMissingParams(missing_keys: List[str]) -> str:
237
+ """
238
+ Generates a human-readable prompt to ask user for missing fields.
239
+ """
240
+ # Create context-aware prompts for specific missing fields
241
+ context_details = {
242
+ "chapters_of_the_test": "such as Math, Science, History, etc.",
243
+ "questions_per_chapter": "the number of questions for each chapter",
244
+ "difficulty_distribution": "as percentages or numbers (easy, medium, hard)",
245
+ "test_duration": "in minutes",
246
+ "test_date": "when the test will be given",
247
+ "test_time": "the time of day for the test"
248
+ }
249
+
250
+ # Create a more specific prompt based on what's missing
251
+ if len(missing_keys) == 1:
252
+ key = missing_keys[0]
253
+ prompt = f"Please provide the {key.replace('_', ' ')} {context_details.get(key, '')}."
254
+ else:
255
+ formatted_missing = [f"{key.replace('_', ' ')} ({context_details.get(key, '')})" for key in missing_keys]
256
+ prompt = f"The following test details are still needed: {', '.join(formatted_missing)}."
257
+
258
+ messages = [
259
+ {"role": "system", "content": "You are a helpful assistant who creates clear, concise questions to collect missing test setup information. Keep your response under 2 sentences and focus only on what's missing."},
260
+ {"role": "user", "content": prompt}
261
+ ]
262
+
263
+ try:
264
+ response = openai.chat.completions.create(
265
+ model="gpt-4o-mini",
266
+ messages=messages,
267
+ temperature=0.3
268
+ )
269
+ return response.choices[0].message.content.strip()
270
+ except Exception as e:
271
+ print(f"Error generating prompt for missing values: {e}")
272
+ return f"Please provide the following missing information: {', '.join(missing_keys)}."
273
+
274
+
275
+ @app.on_event("startup")
276
+ async def startup_event():
277
+ # Set up OpenAI API key from environment variable
278
+ openai.api_key = os.getenv("OPENAI_API_KEY")
279
+ if not openai.api_key:
280
+ print("⚠️ WARNING: OPENAI_API_KEY environment variable not set.")
281
+
282
+
283
+ @app.get("/")
284
+ async def root():
285
+ return {"message": "Test Creation Agent API is running"}
286
+
287
+
288
+ @app.post("/chat")
289
+ async def chat(user_input: UserInput):
290
+ session_id = user_input.session_id
291
+
292
+ # Initialize session if it doesn't exist
293
+ if session_id not in sessions:
294
+ sessions[session_id] = SessionState()
295
+
296
+ session = sessions[session_id]
297
+
298
+ # If this is the first message, send a welcome message
299
+ if session.attempt_count == 0:
300
+ session.attempt_count += 1
301
+ return {
302
+ "response": "πŸ‘‹ Welcome! Please provide the test setup details. I need: chapters, questions per chapter, difficulty distribution, test duration, date, and time.",
303
+ "session_state": {
304
+ "params": session.params,
305
+ "completed": False
306
+ }
307
+ }
308
+
309
+ # Process user input to extract parameters
310
+ session.params = await llm_extractParams(user_input.message, session.params)
311
+ session.attempt_count += 1
312
+
313
+ # Check if we have all required parameters
314
+ missing = gate(session.params)
315
+
316
+ # If we have all parameters or exceeded max attempts, return completion
317
+ max_attempts = 10
318
+ if not missing or session.attempt_count > max_attempts:
319
+ session.completed = True
320
+ if not missing:
321
+ result = "βœ… All test parameters are now complete:"
322
+ else:
323
+ result = "⚠️ Some parameters could not be filled after multiple attempts:"
324
+
325
+ # Format the parameters as a readable string
326
+ for k, v in session.params.items():
327
+ result += f"\n- {k.replace('_', ' ').title()}: {v or 'Not provided'}"
328
+
329
+ return {
330
+ "response": result,
331
+ "session_state": {
332
+ "params": session.params,
333
+ "completed": True
334
+ }
335
+ }
336
+
337
+ # Otherwise, ask for missing parameters
338
+ follow_up_prompt = await llm_getMissingParams(missing)
339
+
340
+ return {
341
+ "response": follow_up_prompt,
342
+ "session_state": {
343
+ "params": session.params,
344
+ "completed": False
345
+ }
346
+ }
347
+
348
+
349
+ @app.get("/session/{session_id}")
350
+ async def get_session(session_id: str):
351
+ if session_id not in sessions:
352
+ raise HTTPException(status_code=404, detail="Session not found")
353
+
354
+ session = sessions[session_id]
355
+ return {
356
+ "params": session.params,
357
+ "completed": session.completed,
358
+ "attempt_count": session.attempt_count
359
+ }
360
+
361
+
362
+ @app.delete("/session/{session_id}")
363
+ async def delete_session(session_id: str):
364
+ if session_id in sessions:
365
+ del sessions[session_id]
366
+ return {"message": "Session deleted successfully"}
367
+
368
+
369
+ @app.post("/reset")
370
+ async def reset_session(user_input: UserInput):
371
+ session_id = user_input.session_id
372
+ sessions[session_id] = SessionState()
373
+
374
+ return {
375
+ "response": "Session reset. πŸ‘‹ Welcome! Please provide the test setup details. I need: chapters, questions per chapter, difficulty distribution, test duration, date, and time.",
376
+ "session_state": {
377
+ "params": sessions[session_id].params,
378
+ "completed": False
379
+ }
380
+ }
381
+
382
+
383
+ if __name__ == "__main__":
384
+ import uvicorn
385
+ uvicorn.run("app:app", host="0.0.0.0", port=int(os.getenv("PORT", 8000)), reload=True)
gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
requirements (1).txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.103.1
2
+ uvicorn==0.23.2
3
+ openai==1.6.1
4
+ pydantic==2.3.0
5
+ python-dotenv==1.0.0
6
+ gunicorn==21.2.0