Skip to content

Instantly share code, notes, and snippets.

@kibolho
Created December 4, 2025 21:35
Show Gist options
  • Select an option

  • Save kibolho/e31e0dddb90f2608a803666043c7b057 to your computer and use it in GitHub Desktop.

Select an option

Save kibolho/e31e0dddb90f2608a803666043c7b057 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""
This script demonstrates how the client.models.generate_content() call is structured
with mock data, including:
- Text input as user message
- Mock current_program
- Same prompt structure
- Same configuration parameters
Can be run independently:
python simulate_programming_converter.py
"""
import base64
import json
import mimetypes
import os
import time
from io import BytesIO
from typing import Literal, get_args
from pydantic import BaseModel, Field
from httpx import AsyncHTTPTransport
from google import genai
from google.genai import types as genai_types
from google.genai.types import (
Content,
GenerateContentConfig,
HarmBlockThreshold,
HarmCategory,
Part,
PartDict,
SafetySetting,
ThinkingConfig,
)
# Constants (from project.ai.converter.constants)
DEFAULT_PROJECT_ID = "superset-api-472315"
MODEL_NAME = "gemini-2.5-flash"
DEFAULT_LOCATION = "us-central1"
USER_TEXT = f"""Basic structure
Frequency: 2–3 non‑consecutive days per week (for example, Monday, Wednesday, Friday).​
Sets and reps: 1–3 sets of 8–15 reps per exercise, resting 1–2 minutes between sets.​
Intensity: Choose a weight that makes the last 2–3 reps challenging but still allows perfect technique.​
Warm‑up (5–10 minutes)
3–5 minutes of easy cardio such as brisk walking, cycling, or treadmill at low intensity.​
Dynamic movements: arm circles, leg swings, and gentle torso twists to prepare the joints.​
Full‑body workout (beginner)
Do this circuit 2–3 times per week with at least one rest day between sessions.​
Squat to chair or bodyweight squat: 2–3 sets of 8–12 reps.​
Push‑up (wall or knee if needed): 2–3 sets of 6–10 reps.​
Hip hinge (dumbbell or bodyweight Romanian deadlift): 2–3 sets of 8–12 reps.​
Row (resistance band, machine, or dumbbells): 2–3 sets of 8–12 reps.​
Overhead shoulder press (light dumbbells or bands): 2–3 sets of 8–12 reps.​
Core: dead bug, bird dog, or basic plank for 15–30 seconds, 2–3 rounds.​
Rest 60–90 seconds between sets and exercises.​
Optional beginner cardio
On 2–3 other days, add light cardio such as brisk walking, cycling, or elliptical for 20–30 minutes at an easy to moderate pace.​
If you share your age, current activity level, any injuries, and whether you train at home or gym (and available equipment), a more tailored plan can be created."""
DEFAULT_SAFETY_SETTINGS = [
SafetySetting(**kwargs) # type: ignore
for kwargs in [
{
"category": HarmCategory.HARM_CATEGORY_HARASSMENT,
"threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
"category": HarmCategory.HARM_CATEGORY_HATE_SPEECH,
"threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
"category": HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
"threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
"category": HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
"threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
]
]
def setup_google_credentials():
"""Setup Google Cloud credentials from environment variables.
Same logic as compose/production/django/entrypoint lines 56-63:
- If GOOGLE_APPLICATION_CREDENTIALS_JSON is set, write it to a temp file
- Set GOOGLE_APPLICATION_CREDENTIALS to point to that file
- Clear GOOGLE_APPLICATION_CREDENTIALS_JSON
"""
# Check for JSON credentials in environment, or use hardcoded dict
# Set GOOGLE_APPLICATION_CREDENTIALS to file path (same as entrypoint: export GOOGLE_APPLICATION_CREDENTIALS=/tmp/google/service-key.json)
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "./service-key.json"
# Set environment variables for Vertex AI (same as programming_converter.py)
os.environ["GOOGLE_CLOUD_PROJECT"] = os.getenv(
"GOOGLE_CLOUD_PROJECT", DEFAULT_PROJECT_ID
)
os.environ["GOOGLE_CLOUD_LOCATION"] = os.getenv(
"GOOGLE_CLOUD_LOCATION", DEFAULT_LOCATION
)
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "true"
def get_genai_client():
"""Get or create a GenAI client (same as in programming_converter.py)."""
setup_google_credentials()
client = genai.Client(
vertexai=True,
http_options=genai_types.HttpOptions(
async_client_args={"transport": AsyncHTTPTransport()}
),
)
return client
# Schema definitions (from project.ai.converter.programming_schema)
# Only define if Pydantic is available
GroupType = Literal["circuit", "warmup", "cooldown", "custom"]
GROUP_TYPES = list(get_args(GroupType))
class RPE(BaseModel):
"""Rate of perceived exertion (RPE) or reps in reserve (RIR)"""
value: str = Field(description="The RPE or RIR value")
variant: Literal["rpe", "rir"] = Field(description="Whether this is RPE or RIR")
class Weight(BaseModel):
"""Weight specification with units"""
value: str = Field(
description="The weight value as a number string (e.g., '135', '60')"
)
units: Literal["lb", "kg"] = Field(
description="The unit (lb/kg) is determined by the program-level weight_unit"
)
class ProgramWorkoutExerciseSimple(BaseModel):
"""Any exercise with sets and reps or time with that info represented in metadata"""
type: Literal["exercise"] = Field(default="exercise")
exercise_name: str = Field(description="The name of the exercise")
sets: str | None = Field(
default=None,
description=(
"Number of sets. Extract from patterns like '3 x 5', '3x5', or '3×5' as sets='3'. "
"If a list is given like '3 x 10, 8, 6', sets='3'. Store as a string."
),
)
reps: str | None = Field(
default=None,
description=(
"Repetitions per set, or a duration. Always include when the source specifies reps/time. "
"Map text to reps as follows: "
"'3 x 5': reps='5'; '3 x 10, 8, 6': reps='10, 8, 6'; '3 x 8e': reps='8e' (per-side marked with 'e'); "
"'3 x 8-10': reps='8-10'; '3 x 1 minute': reps='1 minute'; "
"'3 x 8 @ 85%': reps='8' and put '@ 85%' into notes. "
"Accept 'x', 'X', or '×' as the separator. Store as a string."
),
)
notes: str | None = Field(
default=None,
max_length=100,
description=(
"Use the notes property to put any information about an exercise that doesn't match "
"any of the other properties, like @ 85%, tempo cues, drop sets, or narrative instructions."
),
)
rest: str | None = Field(
default=None,
description="The rest value as a number string (e.g., '1m', '30s')",
)
rpe: RPE | None = Field(
default=None,
description=(
"Rate of perceived exertion (RPE) or reps in reserve (RIR). Supports multiple formats: "
"1) Fixed RPE: '75%' or '7.5' for consistent effort across all sets; "
"2) RPE range: '70-80%' or '7-8' for flexibility in effort level; "
"3) Comma-separated: '60, 70, 80%' for different effort per set. "
"RPE is typically on a scale of 1-10, while RIR is an integer representing reps in reserve."
),
)
weight: Weight | None = Field(
default=None,
description=(
"The weight value with units. Supports multiple formats: "
"1) Fixed weight: '35' or '35+' for consistent weight across all sets; "
"2) Percentage-based: '70%' or '70-80%' based on one-rep max; "
"3) Weight range: '35-55' for flexibility in weight selection; "
"4) Bodyweight: 'BW' to specify bodyweight exercises; "
"5) Comma-separated: '35, 45, 55' for different weights per set. "
"If you can't find a weight value, omit this field."
),
)
class ProgramWorkoutExerciseFreeform(BaseModel):
"""An exercise that does not map to the formatted metadata, but instead has freeform text"""
type: Literal["freeform"] = Field(default="freeform")
exercise_name: str = Field(description="The name of the exercise")
description: str = Field(
description="This can be multi-line and even bulleted text"
)
class ProgramWorkoutExerciseGroup(BaseModel):
"""A grouping of exercises within a workout that are meant to be completed together
(as a cooldown, warmup, or custom type) or performed in rounds (as a circuit)"""
type: Literal["group"] = Field(default="group")
group_type: GroupType = Field(description="The type of group")
title: str = Field(description="A descriptive title for this group of exercises")
rounds: int | None = Field(
default=None,
description='Non-null only for "circuit" types, specify the number of times to repeat the circuit',
)
exercises: list[ProgramWorkoutExerciseSimple] = Field(
description="The specific exercises within this group"
)
class ProgramWorkout(BaseModel):
"""A single workout within a program"""
name: str = Field(description="The name of the workout")
day_offset: int = Field(
description=(
"The number of days from the first day of the program (0-indexed) that this workout "
"is meant to be performed. This number must be unique for each workout in a program."
)
)
workout_exercises: list[
ProgramWorkoutExerciseSimple
| ProgramWorkoutExerciseFreeform
| ProgramWorkoutExerciseGroup
] = Field(description="The exercises in this workout")
class CoachProgram(BaseModel):
"""A workout routine with a name and a list of workouts"""
name: str = Field(description="The name of the program")
weight_unit: Literal["lb", "kg"] = Field(
description="The weight unit used throughout the program"
)
workouts: list[ProgramWorkout] = Field(description="The workouts in this program")
def create_mock_current_program() -> str:
"""Create a mock current_program as a JSON string."""
mock_program = {
"name": "Superset Sheets",
"type": "library",
"weight_unit": "lb",
"effort_variant": "rpe",
"rir_tracking": None,
"assignment_type": None,
"expiration_date": None,
"workouts": [
{
"name": "Workout 1",
"description": "",
"columns": [
{
"type": "sets"
},
{
"type": "reps"
},
{
"type": "weight",
"units": "lb"
},
{
"type": "rpe"
},
{
"type": "rest"
},
{
"type": "notes"
}
],
"day_offset": 0,
"workout_exercises": [
{
"uuid": "5b0eaf98-a5f1-4298-aafc-dfc6932a6538",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "sets",
"value": "3"
},
{
"type": "reps",
"value": "12, 10, 8"
},
{
"type": "notes",
"value": "1-3-1 tempo"
},
{
"type": "rpe",
"value": "80%",
"variant": "rpe"
}
],
"exercise_id": 69,
"exercise_name": "Barbell Bench Press",
"circuit_uuid": None
},
{
"uuid": "1a2577f1-064c-4543-a96f-af1aa9695918",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "sets",
"value": "3"
},
{
"type": "reps",
"value": "12"
},
{
"type": "notes",
"value": "Keep your heels down and drive your knees out over your toes"
},
{
"type": "rest",
"value": "60s"
},
{
"type": "weight",
"value": "95, 115, 135",
"units": "lb"
}
],
"exercise_id": 31,
"exercise_name": "Back Squat",
"circuit_uuid": None
},
{
"uuid": "6ef4c42f-b6e9-4532-b17d-1a2bd277721a",
"exercise_name": "Circuit",
"type": "circuit",
"position": 2,
"metadata": [
{
"type": "sets",
"value": "3"
}
]
},
{
"uuid": "23ac37f9-0e45-47d3-adea-c12d223004f9",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "sets",
"value": "1"
},
{
"type": "reps",
"value": "20s"
},
{
"type": "weight",
"value": "40",
"units": "lb"
}
],
"exercise_id": 527,
"exercise_name": "Kettlebell Swing",
"circuit_uuid": "6ef4c42f-b6e9-4532-b17d-1a2bd277721a"
},
{
"uuid": "6cd2d009-4600-4f4f-afac-b1e515295dca",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "sets",
"value": "1"
},
{
"type": "reps",
"value": "AMRAP"
},
{
"type": "weight",
"value": "BW",
"units": "lb"
}
],
"exercise_id": 489,
"exercise_name": "Hanging Leg Raise",
"circuit_uuid": "6ef4c42f-b6e9-4532-b17d-1a2bd277721a"
},
{
"uuid": "5cbb02d9-54da-4dbd-a51d-c863057f0d7b",
"type": "freeform",
"description": "As many rounds as possible of...21 Toes to bar15 Thrusters9 Burpees10 minute time cap",
"position": 3,
"metadata": [],
"exercise_name": "Finisher"
},
{
"uuid": "a74e1f6b-255b-4bea-9d22-95b035976696",
"exercise_name": "Cool Down",
"type": "cooldown",
"position": 4,
"metadata": [
{
"type": "sets",
"value": "1"
}
]
},
{
"uuid": "a7c28a37-5c8f-42cb-932d-6d9902f57ce5",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "sets",
"value": "3"
},
{
"type": "reps",
"value": "5"
}
],
"exercise_id": 279,
"exercise_name": "Cat Cows",
"circuit_uuid": "a74e1f6b-255b-4bea-9d22-95b035976696"
},
{
"uuid": "17cb3203-5710-4be9-891c-b08c21e2e1b8",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "sets",
"value": "3"
},
{
"type": "reps",
"value": "20s"
}
],
"exercise_id": 223,
"exercise_name": "Butterfly Stretch",
"circuit_uuid": "a74e1f6b-255b-4bea-9d22-95b035976696"
}
],
"uuid": "9bddaef0-be60-4225-8e5e-bbbdcdb30915"
},
{
"name": "Workout 2",
"description": "",
"columns": [
{
"type": "sets"
},
{
"type": "reps"
},
{
"type": "weight",
"units": "lb"
},
{
"type": "rpe"
},
{
"type": "rest"
},
{
"type": "notes"
}
],
"day_offset": 1,
"workout_exercises": [
{
"uuid": "9d3bd1c8-e1f5-4eff-967a-34ed6778f27b",
"exercise_name": "Block 1",
"type": "circuit",
"position": 0,
"metadata": [
{
"type": "sets",
"value": "3"
}
]
},
{
"uuid": "4a650d80-a185-4a01-90a8-623d62ff5545",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "reps",
"value": "10"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 613,
"exercise_name": "Medicine Ball Wall Slam",
"circuit_uuid": "9d3bd1c8-e1f5-4eff-967a-34ed6778f27b"
},
{
"uuid": "3f36dc96-4281-4f59-9330-ef1b968cfff2",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "reps",
"value": "30s"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Hang",
"circuit_uuid": "9d3bd1c8-e1f5-4eff-967a-34ed6778f27b"
},
{
"uuid": "d4a07268-32c4-4f33-8439-5c2ec508458a",
"type": "exercise",
"position": 2,
"metadata": [
{
"type": "reps",
"value": "1:00"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Rower",
"circuit_uuid": "9d3bd1c8-e1f5-4eff-967a-34ed6778f27b"
},
{
"uuid": "0060bf29-00e8-4e00-bef4-51b0179229a2",
"type": "exercise",
"position": 3,
"metadata": [
{
"type": "reps",
"value": "20s"
},
{
"type": "notes",
"value": "each side"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 919,
"exercise_name": "Single-Arm Walking Dumbbell Farmer’s Carry",
"circuit_uuid": "9d3bd1c8-e1f5-4eff-967a-34ed6778f27b"
},
{
"uuid": "197d5cac-55ab-4437-ab36-f8a82c842bad",
"exercise_name": "Block 2",
"type": "circuit",
"position": 1,
"metadata": [
{
"type": "sets",
"value": "4"
}
]
},
{
"uuid": "967bd471-c5b7-4c4f-a7f7-7994b742bf0a",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "reps",
"value": "30s"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Crossover Footspeed",
"circuit_uuid": "197d5cac-55ab-4437-ab36-f8a82c842bad"
},
{
"uuid": "37d19b4c-ce69-483d-b0a3-0357807ad065",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "reps",
"value": "40 yds"
},
{
"type": "notes",
"value": "heavy"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 957,
"exercise_name": "Sled Push",
"circuit_uuid": "197d5cac-55ab-4437-ab36-f8a82c842bad"
},
{
"uuid": "75bf9582-e081-4b63-a3df-09c90b8724e3",
"type": "exercise",
"position": 2,
"metadata": [
{
"type": "reps",
"value": "15"
},
{
"type": "notes",
"value": "each arm/side"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 865,
"exercise_name": "Single-Arm Band Row",
"circuit_uuid": "197d5cac-55ab-4437-ab36-f8a82c842bad"
},
{
"uuid": "f023387b-0a1b-4983-b8ab-c8c39752c42c",
"type": "exercise",
"position": 3,
"metadata": [
{
"type": "reps",
"value": "8"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 349,
"exercise_name": "Deadlift",
"circuit_uuid": "197d5cac-55ab-4437-ab36-f8a82c842bad"
},
{
"uuid": "b577b467-61ee-49ea-b63a-4cab41a0506b",
"exercise_name": "Block 3",
"type": "circuit",
"position": 2,
"metadata": [
{
"type": "sets",
"value": "4"
}
]
},
{
"uuid": "b0583cf6-1d34-4616-be4b-a92b3305ea5f",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "reps",
"value": "10"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 880,
"exercise_name": "Single-Arm Dumbbell Floor Press",
"circuit_uuid": "b577b467-61ee-49ea-b63a-4cab41a0506b"
},
{
"uuid": "1bcb117a-dd8f-44f7-afef-d7036bf5c090",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Curve Fartlek",
"circuit_uuid": "b577b467-61ee-49ea-b63a-4cab41a0506b"
},
{
"uuid": "e21f53c0-2b41-4e34-b479-9db0641a1f04",
"type": "exercise",
"position": 2,
"metadata": [
{
"type": "reps",
"value": "20s"
},
{
"type": "notes",
"value": "each side"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 277,
"exercise_name": "Calf Stretch",
"circuit_uuid": "b577b467-61ee-49ea-b63a-4cab41a0506b"
},
{
"uuid": "78df5ecf-fdce-49e6-8e4d-7967abcd9aa7",
"type": "exercise",
"position": 3,
"metadata": [
{
"type": "reps",
"value": "5"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 292,
"exercise_name": "Chin Ups",
"circuit_uuid": "b577b467-61ee-49ea-b63a-4cab41a0506b"
},
{
"uuid": "9a97d4ae-f4a8-446c-96d6-f061499cfdde",
"exercise_name": "Block 4",
"type": "circuit",
"position": 3,
"metadata": [
{
"type": "sets",
"value": "3"
}
]
},
{
"uuid": "c9c1910f-91a0-428c-9fa7-9f235046ece3",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "reps",
"value": "8"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 372,
"exercise_name": "Dumbbell Alternate Biceps Curl",
"circuit_uuid": "9a97d4ae-f4a8-446c-96d6-f061499cfdde"
},
{
"uuid": "c78ec043-612d-4e57-8e31-f2b7d6078b77",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "reps",
"value": "30s"
},
{
"type": "notes",
"value": "each side"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Pallof Press Isometric Hold",
"circuit_uuid": "9a97d4ae-f4a8-446c-96d6-f061499cfdde"
},
{
"uuid": "6e56d3c0-429b-44be-a422-a209b936cb3b",
"type": "exercise",
"position": 2,
"metadata": [
{
"type": "reps",
"value": "5"
},
{
"type": "notes",
"value": "each leg"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_id": 942,
"exercise_name": "Single-Leg Squat to Box",
"circuit_uuid": "9a97d4ae-f4a8-446c-96d6-f061499cfdde"
},
{
"uuid": "440ba052-4038-4c4b-a9a5-a959fe688b7d",
"type": "exercise",
"position": 3,
"metadata": [
{
"type": "reps",
"value": "20"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Flutter Kicks",
"circuit_uuid": "9a97d4ae-f4a8-446c-96d6-f061499cfdde"
},
{
"uuid": "54f387c4-44c2-4be0-9146-6fa21deb0a01",
"exercise_name": "Block 5",
"type": "circuit",
"position": 4,
"metadata": [
{
"type": "sets",
"value": "3"
}
]
},
{
"uuid": "d71892af-25e6-476f-85f7-68e09362ef79",
"type": "exercise",
"position": 0,
"metadata": [
{
"type": "reps",
"value": "12"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Quadruped Hip Rockers",
"circuit_uuid": "54f387c4-44c2-4be0-9146-6fa21deb0a01"
},
{
"uuid": "64dbfc69-ec13-4569-b911-c5a5b34c0a76",
"type": "exercise",
"position": 1,
"metadata": [
{
"type": "reps",
"value": "5"
},
{
"type": "notes",
"value": "each side"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Active Isolated Hamstring Stretch",
"circuit_uuid": "54f387c4-44c2-4be0-9146-6fa21deb0a01"
},
{
"uuid": "29be751a-b556-4097-be49-0b24c5416305",
"type": "exercise",
"position": 2,
"metadata": [
{
"type": "reps",
"value": "5"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Prev. Ben Johnsons",
"circuit_uuid": "54f387c4-44c2-4be0-9146-6fa21deb0a01"
},
{
"uuid": "ff01ecd8-d895-4e8a-9cc8-088e89a36d0c",
"type": "exercise",
"position": 3,
"metadata": [
{
"type": "reps",
"value": "30s"
},
{
"type": "sets",
"value": "1"
}
],
"exercise_name": "Lat Stretch",
"circuit_uuid": "54f387c4-44c2-4be0-9146-6fa21deb0a01"
}
],
"uuid": "5076adb2-8158-45d8-a831-7c579cb2ff40"
}
]
}
return json.dumps(mock_program, indent=2)
def simulate_api_call():
"""Simulate the client.models.generate_content() call with fake data."""
# Mock current_program (as JSON string, as expected by the function)
current_program = create_mock_current_program()
# Build the prompt with current program context (same as in programming_converter.py)
prompt_parts = [
"You are an expert personal trainer and create workout programs from any input."
]
if current_program:
prompt_parts.append(
"\n\nIMPORTANT: The user has an existing program. Please review the current program structure below "
"and make sure to preserve existing workouts when possible. Only modify or add workouts based on the user's request. "
"Do not override existing workouts unless explicitly requested.\n\n"
f"Current program structure:\n{current_program}"
)
prompt = "\n".join(prompt_parts).strip()
# Convert text input to file-like object (same as convert_program does)
infile = BytesIO(USER_TEXT.encode("utf8"))
infile.name = "workout_program.txt"
# Build file part (same as _get_file_part does)
file_data = infile.read()
infile.seek(0)
mime_type, _ = mimetypes.guess_type(infile.name)
if not mime_type:
mime_type = "text/plain"
if not PartDict or not Content:
raise ImportError(
"google-genai types are not available. Install with: pip install google-genai"
)
file_part = PartDict( # type: ignore
inline_data={
"mime_type": mime_type,
"data": base64.b64encode(file_data).decode("utf-8"),
}
)
# Build contents array (same structure as in programming_converter.py)
contents = [
Content( # type: ignore
role="user",
parts=[file_part],
)
]
client = get_genai_client()
print("=" * 80)
print(f"\nModel: {MODEL_NAME}")
print(f"\nPrompt:\n{prompt[:200]}...") # Show first 200 chars
print(f"\nUser Input (text): {USER_TEXT}")
print(f"\nCurrent Program (first 200 chars):\n{current_program[:200]}...")
print("\n" + "=" * 80)
print("CALL PARAMETERS:")
print("=" * 80)
print("\n📡 Making API call...")
try:
# Measure time taken for API call
start_time = time.time()
response = client.models.generate_content( # type: ignore
model=MODEL_NAME,
contents=contents,
config=GenerateContentConfig(
response_mime_type="application/json",
thinking_config=ThinkingConfig( # type: ignore
thinking_budget=-1,
),
system_instruction=[Part.from_text(text=prompt)], # type: ignore
safety_settings=DEFAULT_SAFETY_SETTINGS,
max_output_tokens=16384,
response_schema=CoachProgram,
), # type: ignore
)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"\n✅ Real API call completed successfully!")
print(
f"⏱️ Time taken: {elapsed_time:.2f} seconds ({elapsed_time * 1000:.0f} ms)"
)
response_text = getattr(response, "text", "") or ""
print(f"\nResponse text (first 500 chars):\n{response_text[:500]}...")
# Validate response
try:
program = CoachProgram.model_validate_json(response_text)
print("\n✅ Response validated successfully with Pydantic schema!")
print(f"\nProgram name: {program.name}")
print(f"Number of workouts: {len(program.workouts)}")
print(f"Weight unit: {program.weight_unit}")
except Exception as e:
print(f"\n⚠️ Validation error: {e}")
# Try to parse as JSON for debugging
try:
parsed = json.loads(response_text)
print(f" Response is valid JSON with keys: {list(parsed.keys())}")
except:
print(" Response is not valid JSON")
return response
except Exception as e:
print(f"\n❌ API call failed: {e}")
raise
if __name__ == "__main__":
simulate_api_call()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment