aicodingfun's picture
Create app.py
7eacbe0 verified
Raw
History Blame Contribute Delete
23.7 kB
import gradio as gr
import json
import random
import tempfile
from datetime import datetime
from typing import Dict, Tuple, Optional
from PIL import Image
import io
from google import genai
from google.genai import types
import os
import gtts
API_KEY = os.environ.get("GOOGLE_API_KEY")
client = genai.Client(api_key=API_KEY)
# Pet state storage
pet_state = {
"name": "",
"species": "",
"personality": "",
"appearance": "",
"mood": "ๅฟซๆจ‚", # Mood is stored in Chinese, mapped to English for image generation
"hunger": 80,
"energy": 90,
"experience": 0,
"level": 1,
"interaction_history": [],
"battle_stats": {"wins": 0, "losses": 0}
}
def generate_pet_description(custom_input: str = "") -> Dict:
"""Generate pet basic settings using Gemini 2.0 Flash"""
try:
if custom_input.strip():
prompt = f"""
่ซ‹ๆ นๆ“šไฝฟ็”จ่€…ๆ่ฟฐๅ‰ตๅปบไธ€้šปๅฏถๅฏๅคข้ขจๆ ผ็š„ๅฏต็‰ฉ๏ผš{custom_input}
่ซ‹ไปฅ JSON ๆ ผๅผ่ฟ”ๅ›ž๏ผš
{{
"name": "ๅฏต็‰ฉๅ็จฑ",
"species": "็‰ฉ็จฎ้กžๅž‹",
"personality": "ๆ€งๆ ผๆ่ฟฐ",
"appearance": "ๅค–่ง€็‰นๅพตๆ่ฟฐ"
}}
"""
else:
prompt = """
่ซ‹้šจๆฉŸๅ‰ตๅปบไธ€้šปๅฏๆ„›็š„ๅฏถๅฏๅคข้ขจๆ ผๅฏต็‰ฉใ€‚
่ซ‹ไปฅ JSON ๆ ผๅผ่ฟ”ๅ›ž๏ผš
{
"name": "ๅฏต็‰ฉๅ็จฑ",
"species": "็‰ฉ็จฎ้กžๅž‹",
"personality": "ๆ€งๆ ผๆ่ฟฐ",
"appearance": "ๅค–่ง€็‰นๅพตๆ่ฟฐ"
}
่ฆๆฑ‚๏ผšๅ็จฑ่ฆๅฏๆ„›๏ผŒๆ€งๆ ผ่ฆๆœ‰่ถฃ๏ผŒๅค–่ง€่ฆ็”Ÿๅ‹•ๅฝข่ฑกใ€‚
"""
response = client.models.generate_content(
model="gemini-2.5-flash-preview-04-17",
contents=prompt
)
response_text = response.text.strip()
if response_text.startswith('```json'):
response_text = response_text[7:-3]
elif response_text.startswith('```'):
response_text = response_text[3:-3]
pet_data = json.loads(response_text)
return pet_data
except Exception as e:
print(f"Error generating pet description: {e}")
return {
"name": "็šฎๅกไธ˜",
"species": "้›ปๆฐฃ้ผ ๅฏถๅฏๅคข",
"personality": "ๆดปๆฝ‘ๅฅฝๅ‹•๏ผŒๅ–œๆญกไบคๆœ‹ๅ‹",
"appearance": "้ปƒ่‰ฒๆฏ›้ซฎ๏ผŒ็ด…่‰ฒ่‡‰้ ฐ๏ผŒ้•ท้•ทๅฐพๅทด"
}
def generate_pet_image(description: str, mood: str = "ๅฟซๆจ‚", context_desc: str = "") -> Optional[Image.Image]:
"""Generate pet image using Gemini 2.0 Flash, including context"""
try:
mood_map = {
"ๅฟซๆจ‚": "happy and cheerful",
"้ฃข้ค“": "hungry and tired",
"่ˆˆๅฅฎ": "excited and energetic",
"็–ฒๆ†Š": "sleepy and tired",
"็”Ÿๆฐฃ": "angry and upset",
"ๆปฟ่ถณ": "satisfied and content"
}
mood_desc = mood_map.get(mood, "happy")
full_description = f"{description}"
if context_desc:
full_description += f", {context_desc}"
image_prompt = f"""
่ซ‹็นช่ฃฝไธ€ๅผตๅฏๆ„›็š„ๅฏถๅฏๅคข้ขจๆ ผๅฏต็‰ฉๆ’ๅœ–๏ผŒ็ดฐ็ฏ€ๅฆ‚ไธ‹๏ผš
- ๆ่ฟฐ๏ผš {full_description}
- ๆƒ…็ท’๏ผš {mood_desc}
- ้ขจๆ ผ๏ผš ่‰ฒๅฝฉ้ฎฎ่ฑ”ใ€ๅฏๆ„›ๅ‹ๅ–„็š„ๅ‹•ๆผซ้ขจๆ ผ๏ผŒๅ‘ˆ็พๅฏต็‰ฉๅ…จ่บซ
- ่ƒŒๆ™ฏ๏ผš ็ฐกๅ–ฎใ€ๆŸ”ๅ’Œ็š„่‰ฒๅฝฉใ€‚ๅ„ชๅ…ˆ่€ƒๆ…ฎ้€ๆ˜Ž่ƒŒๆ™ฏ๏ผŒ่‹ฅไธๅฏ่กŒๅ‰‡ไฝฟ็”จ็ฐกๅ–ฎ่ƒŒๆ™ฏ
- ๅ“่ณช๏ผš ้ซ˜่งฃๆžๅบฆใ€็ดฐ็ฏ€่ฑๅฏŒ
- ้•ทๅฏฌๆฏ”๏ผšๆญฃๆ–นๅฝข (1:1)
- **่ซ‹ๅ‹ฟ**ๅœจๅœ–็‰‡ไธญๅŠ ๅ…ฅไปปไฝ•ๆ–‡ๅญ—ใ€‚
"""
print(f"Generating image with prompt: {image_prompt}")
image_response = client.models.generate_content(
model="gemini-2.0-flash-preview-image-generation",
# model="imagen-3.0-generate-002",
contents=image_prompt,
config=types.GenerateContentConfig(
response_modalities=['TEXT', 'IMAGE']
)
)
if image_response and image_response.candidates and image_response.candidates[0].content.parts:
for part in image_response.candidates[0].content.parts:
if part.inline_data and part.inline_data.mime_type.startswith('image/'):
image_data_base64 = part.inline_data.data
img = Image.open(io.BytesIO(image_data_base64))
img = img.resize((512, 512), Image.Resampling.LANCZOS)
print("Image generated successfully.")
return img
print("Image data not found in any response parts or not an image.")
return None
else:
print("Image generation response is empty or malformed.")
return None
except Exception as e:
print(f"Error generating pet image: {e}")
return None
def generate_tts_audio(text: str, lang: str = "zh-TW") -> Optional[str]:
"""Generate text-to-speech audio file"""
try:
tts = gtts.gTTS(text=text, lang=lang, slow=False)
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3")
tts.save(temp_file.name)
return temp_file.name
except Exception as e:
print(f"TTS generation failed: {e}")
return None
def chat_with_pet(user_message: str) -> Tuple[str, Optional[str], None, Optional[Image.Image]]:
"""Chat with the pet"""
global pet_state
try:
if not pet_state["name"]:
return "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", None, None, None
context = f"""
ไฝ ๆ˜ฏไธ€้šปๅๅซ {pet_state['name']} ็š„ {pet_state['species']}๏ผŒๆ“ๆœ‰ {pet_state['personality']} ็š„็จ็‰นๅ€‹ๆ€งใ€‚
ไฝ ็พๅœจ็š„ๅฟƒๆƒ…ๆ˜ฏ {pet_state['mood']}๏ผŒๆ„Ÿ่ฆบ {pet_state['hunger']}/100 ้ฃข้ค“๏ผŒ็ฒพๅŠ›ๅ‰‡ๆœ‰ {pet_state['energy']}/100ใ€‚
่ซ‹ไฝ ๅฎŒๅ…จ่žๅ…ฅ้€™ๅ€‹่ง’่‰ฒ๏ผŒไปฅๆœ€็ฌฆๅˆไฝ ็•ถๅ‰็‹€ๆ…‹๏ผˆๅฟƒๆƒ…ใ€้ฃข้ค“ใ€็ฒพๅŠ›๏ผ‰ๅ’Œๅ€‹ๆ€ง็š„ๅฏๆ„›ใ€ๅ‹ๅ–„ๅฃๅป๏ผŒๅ›žๆ‡‰ไธปไบบๅฐไฝ ่ชช็š„่ฉฑ๏ผš"{user_message}"ใ€‚
ๅ›žๆ‡‰่ฆๆฑ‚๏ผš
1. ไฟๆŒ่ง’่‰ฒ่จญๅฎšไธฆ่žๅ…ฅ็•ถๅ‰็‹€ๆ…‹ (ๅฟƒๆƒ…ใ€้ฃข้ค“ใ€็ฒพๅŠ›)ใ€‚
2. ไปฅๅฏๆ„›ใ€ๅ‹ๅ–„็š„ๅฏต็‰ฉๅฃๅปๅ›žๆ‡‰ใ€‚
3. ๅ›žๆ‡‰้•ทๅบฆ็ด„ๅœจ 20 ่‡ณ 50 ๅญ—ไน‹้–“ใ€‚
4. ๅฏไปฅ่กจ้”ไฝ ็š„ๆƒ…ๆ„Ÿๅ’Œ้œ€ๆฑ‚ใ€‚
5. ไฝฟ็”จ็น้ซ”ไธญๆ–‡ใ€‚
"""
response = client.models.generate_content(
model="gemini-2.5-flash-preview-04-17",
contents=context
)
pet_response = response.text.strip()
pet_state["interaction_history"].append({
"timestamp": datetime.now().strftime("%H:%M:%S"),
"user": user_message,
"pet": pet_response,
"type": "chat"
})
audio_file = generate_tts_audio(pet_response)
image_description = f"{pet_state['species']} - {pet_state.get('appearance', '')}" if pet_state['appearance'] else pet_state['species']
new_image = generate_pet_image(
image_description,
pet_state["mood"],
context_desc=f"chatting with the user, response: {pet_response}"
)
return pet_response, audio_file, None, new_image
except Exception as e:
error_msg = f"Chat function error: {e}"
print(error_msg)
return "ๅ—š... ๆˆ‘็พๅœจ่ชชไธๅ‡บ่ฉฑไพ†...", None, None, None
def feed_pet(food_type: str) -> Tuple[str, str, Optional[str], Optional[Image.Image]]:
"""Feed the pet"""
global pet_state
try:
if not pet_state["name"]:
return "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", None, None
if not food_type:
foods = ["ๆจนๆžœ", "ๅฏถๅฏๅคข้ฃŸ็‰ฉ", "่˜‹ๆžœ", "้ค…ไนพ", "็ณ–ๆžœ"]
food_type = random.choice(foods)
hunger_increase = random.randint(15, 25)
pet_state["hunger"] = min(100, pet_state["hunger"] + hunger_increase)
if pet_state["hunger"] > 80:
pet_state["mood"] = "ๅฟซๆจ‚"
elif pet_state["hunger"] > 50:
pet_state["mood"] = "ๆปฟ่ถณ"
context = f"""
{pet_state['name']} ๅ‰›ๅ‰›ๅƒไบ† {food_type}๏ผŒ้ฃข้ค“็จ‹ๅบฆๅพžไน‹ๅ‰ๆๅ‡ๅˆฐ {pet_state['hunger']}/100ใ€‚
่ซ‹ไปฅๅฏต็‰ฉ็š„่ง’ๅบฆๆ่ฟฐไบซ็”จ้ฃŸ็‰ฉ็š„ๆ„Ÿๅ—ๅ’Œๅๆ‡‰ใ€‚
่ฆๆฑ‚๏ผš
1. 50-80ๅญ—็š„ๅฏๆ„›ๅ›žๆ‡‰
2. ่กจ้”ๅฐ้ฃŸ็‰ฉ็š„ๅ–œๆ„›
3. ็”จ็น้ซ”ไธญๆ–‡
4. ็ฌฆๅˆ {pet_state['personality']} ็š„ๆ€งๆ ผ
"""
response = client.models.generate_content(
model="gemini-2.5-flash-preview-04-17",
contents=context
)
pet_response = response.text.strip()
pet_state["interaction_history"].append({
"timestamp": datetime.now().strftime("%H:%M:%S"),
"action": f"้คต้ฃŸ {food_type}",
"result": pet_response,
"type": "feed"
})
image_description = f"{pet_state['species']} - {pet_state.get('appearance', '')}" if pet_state['appearance'] else pet_state['species']
new_image = generate_pet_image(
image_description,
pet_state["mood"],
context_desc=f"eating {food_type}, reaction: {pet_response}"
)
status_update = f"็ญ‰็ดš: {pet_state['level']} | ้ฃข้ค“: {pet_state['hunger']}/100 | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
audio_file = generate_tts_audio(pet_response)
return pet_response, status_update, audio_file, new_image
except Exception as e:
error_msg = f"Feed function error: {e}"
print(error_msg)
status_update = f"็ญ‰็ดš: {pet_state['level']} | ้ฃข้ค“: {pet_state['hunger']}/100 | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
return f"ๅ—š... ้คต้ฃŸๆ™‚ๅ‡บไบ†้ปžๅ•้กŒใ€‚({error_msg})", status_update, None, None
def adventure_with_pet() -> Tuple[str, str, Optional[str], Optional[Image.Image]]:
"""Go on an adventure with the pet"""
global pet_state
try:
if not pet_state["name"]:
return "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", None, None
energy_cost = random.randint(10, 20)
pet_state["energy"] = max(0, pet_state["energy"] - energy_cost)
exp_gain = random.randint(5, 15)
pet_state["experience"] += exp_gain
level_up_msg = ""
while pet_state["experience"] >= pet_state["level"] * 100:
pet_state["experience"] -= pet_state["level"] * 100
pet_state["level"] += 1
level_up_msg += f"ๆญๅ–œ๏ผ{pet_state['name']} ๅ‡ๅˆฐไบ† {pet_state['level']} ็ดš๏ผ\n"
level_up_msg = level_up_msg.strip()
context = f"""
{pet_state['name']} ๆญฃๅœจ้€ฒ่กŒไธ€ๆฌกๆŽข้šชๅ†’้šชใ€‚่ซ‹ๅ‰ต้€ ไธ€ๅ€‹ๆœ‰่ถฃ็š„ๆŽข้šชๅ ดๆ™ฏ๏ผš
ๅฏต็‰ฉ่ณ‡่จŠ๏ผš
- ็จฎๆ—๏ผš{pet_state['species']}
- ๆ€งๆ ผ๏ผš{pet_state['personality']}
- ็ญ‰็ดš๏ผš{pet_state['level']}
- ็›ฎๅ‰็ฒพๅŠ›๏ผš{pet_state['energy']}/100
่ซ‹ๆ่ฟฐไธ€ๅ€‹็Ÿญๅฐ็ฒพๅฝฉ็š„ๆŽข้šชๆ•…ไบ‹๏ผˆ80-100ๅญ—๏ผ‰๏ผŒๅŒ…ๅซ๏ผš
1. ๆŽข้šชๅ ดๆ™ฏ
2. ้‡ๅˆฐ็š„ไบ‹ไปถๆˆ–็™ผ็พ
3. ๅฏต็‰ฉ็š„ๅๆ‡‰ๅ’Œๆ”ถ็ฒ
4. ็”จ็น้ซ”ไธญๆ–‡ๆ’ฐๅฏซ
"""
response = client.models.generate_content(
model="gemini-2.5-flash-preview-04-17",
contents=context
)
adventure_story = response.text.strip()
image_description = f"{pet_state['species']} - {pet_state.get('appearance', '')}" if pet_state['appearance'] else pet_state['species']
new_image = generate_pet_image(
image_description,
pet_state["mood"],
context_desc=f"on an adventure: {adventure_story}"
)
if level_up_msg:
adventure_story += f"\n\n๐ŸŽ‰ {level_up_msg}"
adventure_story += f"\n\n๐Ÿ“Š ็ฒๅพ—็ถ“้ฉ—ๅ€ผ: +{exp_gain}"
record_result = adventure_story
pet_state["interaction_history"].append({
"timestamp": datetime.now().strftime("%H:%M:%S"),
"action": "ๆŽข้šช",
"result": record_result,
"type": "adventure",
"exp_gained": exp_gain
})
if pet_state["energy"] > 60:
pet_state["mood"] = "่ˆˆๅฅฎ"
elif pet_state["energy"] > 30:
pet_state["mood"] = "ๆปฟ่ถณ"
else:
pet_state["mood"] = "็–ฒๆ†Š"
audio_file = generate_tts_audio(adventure_story)
status_update = f"็ญ‰็ดš: {pet_state['level']} | ็ถ“้ฉ—: {pet_state['experience']}/{pet_state['level']*100} | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
return adventure_story, status_update, audio_file, new_image
except Exception as e:
error_msg = f"Adventure function error: {e}"
print(error_msg)
status_update = f"็ญ‰็ดš: {pet_state['level']} | ็ถ“้ฉ—: {pet_state['experience']}/{pet_state['level']*100} | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
return "ๅ—š... ๆŽข้šชๆ™‚ๅ‡บไบ†้ปžๅ•้กŒใ€‚", status_update, None, None
def battle_with_pet() -> Tuple[str, str, Optional[str], Optional[Image.Image]]:
"""Battle with the pet"""
global pet_state
try:
if not pet_state["name"]:
return "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", "่ซ‹ๅ…ˆๅ‰ตๅปบไฝ ็š„ๅฏต็‰ฉ๏ผ", None, None
opponents = ["้‡Ž็”Ÿ็šฎๅกไธ˜", "ๅฐ็ซ้พ", "ๅ‚‘ๅฐผ้พœ", "ๅฆ™่›™็จฎๅญ", "ๅฐๆ‹‰้”", "ๆณขๆณข"]
opponent = random.choice(opponents)
pet_power = pet_state["level"] * 10 + random.randint(1, 20)
opponent_power = random.randint(10, 50)
win = pet_power > opponent_power
if win:
pet_state["battle_stats"]["wins"] += 1
result = "ๅ‹ๅˆฉ"
exp_gain = random.randint(10, 20)
pet_state["experience"] += exp_gain
pet_state["mood"] = "่ˆˆๅฅฎ"
else:
pet_state["battle_stats"]["losses"] += 1
result = "ๅคฑๆ•—"
exp_gain = random.randint(3, 8)
pet_state["experience"] += exp_gain
pet_state["mood"] = "็–ฒๆ†Š"
level_up_msg = ""
while pet_state["experience"] >= pet_state["level"] * 100:
pet_state["experience"] -= pet_state["level"] * 100
pet_state["level"] += 1
level_up_msg += f"ๆญๅ–œ๏ผ{pet_state['name']} ๅ‡ๅˆฐไบ† {pet_state['level']} ็ดš๏ผ\n"
level_up_msg = level_up_msg.strip()
energy_cost = random.randint(15, 25)
pet_state["energy"] = max(0, pet_state["energy"] - energy_cost)
context = f"""
{pet_state['name']} ๅ‰›ๅ‰›ๅ’Œ {opponent} ้€ฒ่กŒไบ†ไธ€ๅ ดๅฐๆˆฐ๏ผŒ็ตๆžœๆ˜ฏ{result}ใ€‚
่ซ‹ๆ่ฟฐ้€™ๅ ดๆˆฐ้ฌฅ็š„้Ž็จ‹ๅ’Œ็ตๆžœ๏ผˆ80-120ๅญ—๏ผ‰๏ผš
1. ๆˆฐ้ฌฅๅ ด้ขๆ่ฟฐ
2. ไฝฟ็”จ็š„ๆ‹›ๅผ (ๅฏถๅฏๅคข้ขจๆ ผ)
3. ๆˆฐ้ฌฅ็ตๆžœ
4. {pet_state['name']} ็š„ๅๆ‡‰
5. ็”จ็น้ซ”ไธญๆ–‡ๆ’ฐๅฏซ
6. ็ฌฆๅˆ {pet_state['personality']} ็š„ๆ€งๆ ผ
"""
response = client.models.generate_content(
model="gemini-2.5-flash-preview-04-17",
contents=context
)
battle_story = response.text.strip()
image_description = f"{pet_state['species']} - {pet_state.get('appearance', '')}" if pet_state['appearance'] else pet_state['species']
new_image = generate_pet_image(
image_description,
pet_state["mood"],
context_desc=f"in a battle against {opponent}, based on the story: {battle_story}"
)
battle_story += f"\n\n๐Ÿ“Š ็ฒๅพ—็ถ“้ฉ—ๅ€ผ: +{exp_gain}"
if level_up_msg:
battle_story += f"\n\n๐ŸŽ‰ {level_up_msg}"
record_result = f"{result} - {battle_story}"
pet_state["interaction_history"].append({
"timestamp": datetime.now().strftime("%H:%M:%S"),
"action": f"ๅฐๆˆฐ vs {opponent}",
"result": record_result,
"type": "battle"
})
audio_file = generate_tts_audio(battle_story)
status_update = f"็ญ‰็ดš: {pet_state['level']} | ็ถ“้ฉ—: {pet_state['experience']}/{pet_state['level']*100} | ๅ‹ๅ ด: {pet_state['battle_stats']['wins']} | ๆ•—ๅ ด: {pet_state['battle_stats']['losses']} | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
return battle_story, status_update, audio_file, new_image
except Exception as e:
error_msg = f"Battle function error: {e}"
print(error_msg)
status_update = f"็ญ‰็ดš: {pet_state['level']} | ็ถ“้ฉ—: {pet_state['experience']}/{pet_state['level']*100} | ๅ‹ๅ ด: {pet_state['battle_stats']['wins']} | ๆ•—ๅ ด: {pet_state['battle_stats']['losses']} | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
return f"ๅ—š... ๅฐๆˆฐๆ™‚ๅ‡บไบ†้ปžๅ•้กŒใ€‚", status_update, None, None
def initialize_pet(custom_input: str) -> Tuple[Optional[Image.Image], str, str, str, Optional[str]]:
"""Initialize the pet"""
global pet_state
pet_data = generate_pet_description(custom_input)
pet_state.update({
"name": pet_data.get("name", "ๆœช็Ÿฅๅฏต็‰ฉ"),
"species": pet_data.get("species", "ๆœช็Ÿฅ็‰ฉ็จฎ"),
"personality": pet_data.get("personality", "ๆบซๅ’Œ"),
"appearance": pet_data.get("appearance", ""),
"mood": "ๅฟซๆจ‚",
"hunger": 80,
"energy": 90,
"experience": 0,
"level": 1,
"interaction_history": [],
"battle_stats": {"wins": 0, "losses": 0}
})
image_description = f"{pet_state['species']} - {pet_state['appearance']}" if pet_state['appearance'] else pet_state['species']
pet_image = generate_pet_image(image_description, pet_state["mood"])
welcome_msg = f"๐ŸŽ‰ ๆญก่ฟŽไพ†ๅˆฐๅฏถๅฏๅคขไธ–็•Œ๏ผ\n\n" \
f"โœจ ๅ็จฑ๏ผš{pet_state['name']}\n" \
f"๐Ÿพ ็จฎๆ—๏ผš{pet_state['species']}\n" \
f"๐Ÿ’ญ ๆ€งๆ ผ๏ผš{pet_state['personality']}\n" \
f"๐Ÿ˜Š ๅฟƒๆƒ…๏ผš{pet_state['mood']}\n\n" \
f"็พๅœจไฝ ๅฏไปฅๅ’Œ {pet_state['name']} ไบ’ๅ‹•ไบ†๏ผ"
status_info = f"็ญ‰็ดš: {pet_state['level']} | ้ฃข้ค“: {pet_state['hunger']}/100 | ็ฒพๅŠ›: {pet_state['energy']}/100 | ๅฟƒๆƒ…: {pet_state['mood']}"
audio_file = generate_tts_audio(f"ไฝ ๅฅฝ๏ผๆˆ‘ๆ˜ฏ{pet_state['name']}๏ผŒๅพˆ้ซ˜่ˆˆ่ช่ญ˜ไฝ ๏ผ")
return pet_image, welcome_msg, status_info, "", audio_file
def get_interaction_history() -> str:
"""Get interaction history"""
if not pet_state["interaction_history"]:
return "้‚„ๆฒ’ๆœ‰ไบ’ๅ‹•่จ˜้Œ„ๅ‘ข๏ผๅฟซๅ’Œไฝ ็š„ๅฏต็‰ฉไบ’ๅ‹•ๅง๏ผ"
history_text = f"๐Ÿ“‹ {pet_state['name']} ็š„ไบ’ๅ‹•ๆ—ฅ่ชŒ\n" + "="*50 + "\n\n"
for record in pet_state["interaction_history"][-20:]:
time_stamp = record["timestamp"]
if record["type"] == "chat":
history_text += f"๐Ÿ• {time_stamp} [่Šๅคฉ]\n"
history_text += f"๐Ÿ‘ค ไฝ : {record['user']}\n"
history_text += f"๐Ÿพ {pet_state['name']}: {record['pet']}\n\n"
else:
action_desc = record.get('action', record['type'])
result_desc = record.get('result', '็„ก็ตๆžœๆ่ฟฐ')
history_text += f"๐Ÿ• {time_stamp} [{action_desc}]\n"
history_text += f"๐Ÿ“ {result_desc}\n\n"
return history_text
def create_app():
"""Create the Gradio application"""
with gr.Blocks(
title="Virtual Pokemon Companion",
theme=gr.themes.Soft(),
css="""
.main-container { max-width: 1200px; margin: 0 auto; }
.pet-image { border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); object-fit: contain; }
.status-box { background: linear-gradient(45deg, #ff9a9e, #fecfef); padding: 15px; border-radius: 10px; }
.interaction-box { background: linear-gradient(45deg, #a8edea, #fed6e3); padding: 15px; border-radius: 10px; }
""") as app:
gr.Markdown("""
# ๐ŸŒŸ Virtual Pokemon Companion
### An AI-powered pet system using Google Gemini
""")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("## ๐Ÿพ Your Pet")
pet_image = gr.Image(
label="Pet Image",
height=300,
show_label=False,
elem_classes=["pet-image"],
interactive=False
)
pet_status = gr.Textbox(
label="Pet Status",
value="Please create your pet first!",
interactive=False,
lines=3,
elem_classes=["status-box"]
)
gr.Markdown("### ๐ŸŽฎ Create Pet")
custom_pet_input = gr.Textbox(
label="Custom Pet Traits (Optional)",
placeholder="e.g., A flying blue dragon with a gentle personality..."
)
with gr.Row():
create_random_btn = gr.Button("๐ŸŽฒ Randomize", variant="primary")
create_custom_btn = gr.Button("โœจ Create Custom", variant="secondary")
with gr.Column(scale=1):
gr.Markdown("## ๐Ÿ’ฌ Interaction Area")
chat_output = gr.Textbox(
label="Pet Response",
value="Your pet is waiting to be created...",
interactive=False,
lines=4,
elem_classes=["interaction-box"]
)
audio_output = gr.Audio(
label="Pet Voice",
visible=True,
autoplay=True
)
gr.Markdown("### ๐Ÿ’ญ Chat")
chat_input = gr.Textbox(
label="Talk to your pet",
placeholder="Enter your message here..."
)
chat_btn = gr.Button("๐Ÿ’ฌ Chat", variant="primary")
gr.Markdown("### ๐ŸŽฏ Activities")
with gr.Row():
feed_btn = gr.Button("๐ŸŽ Feed", variant="secondary")
adventure_btn = gr.Button("๐Ÿ—บ๏ธ Adventure", variant="secondary")
with gr.Row():
battle_btn = gr.Button("โš”๏ธ Battle", variant="secondary")
history_btn = gr.Button("๐Ÿ“‹ View Log", variant="secondary")
with gr.Row():
history_output = gr.Textbox(
label="๐Ÿ“œ Interaction Log",
lines=8,
interactive=False,
visible=False,
max_lines=20
)
# Event bindings
create_random_btn.click(
fn=lambda: initialize_pet(""),
outputs=[pet_image, chat_output, pet_status, custom_pet_input, audio_output]
)
create_custom_btn.click(
fn=initialize_pet,
inputs=[custom_pet_input],
outputs=[pet_image, chat_output, pet_status, custom_pet_input, audio_output]
)
chat_btn.click(
fn=chat_with_pet,
inputs=[chat_input],
outputs=[chat_output, audio_output, chat_input, pet_image]
)
chat_input.submit(
fn=chat_with_pet,
inputs=[chat_input],
outputs=[chat_output, audio_output, chat_input, pet_image]
)
feed_btn.click(
fn=lambda: feed_pet(""),
outputs=[chat_output, pet_status, audio_output, pet_image]
)
adventure_btn.click(
fn=adventure_with_pet,
outputs=[chat_output, pet_status, audio_output, pet_image]
)
battle_btn.click(
fn=battle_with_pet,
outputs=[chat_output, pet_status, audio_output, pet_image]
)
def toggle_history():
current_visibility = history_output.visible
new_visibility = not current_visibility
history_content = get_interaction_history() if new_visibility else ""
return gr.update(visible=new_visibility), history_content
history_btn.click(
fn=toggle_history,
outputs=[history_output, history_output]
)
return app
app = create_app()
app.launch()