File size: 4,256 Bytes
2e787a2
eb3e391
2e787a2
eb3e391
 
 
2e787a2
eca7f7a
2e787a2
 
eadc77b
2e787a2
 
 
 
 
 
eca7f7a
 
2e787a2
 
 
 
 
eca7f7a
 
2e787a2
 
 
 
 
 
eca7f7a
2e787a2
 
eca7f7a
33d38e5
eb3e391
2e787a2
 
 
 
 
 
 
 
 
 
 
 
 
eb3e391
2e787a2
 
 
 
 
eb3e391
2e787a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb3e391
2e787a2
eb3e391
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from pydantic import BaseModel, Field, validator
from typing import List, Optional
from core.constants import GameConfig

class Choice(BaseModel):
    id: int
    text: str = Field(description="The text of the choice.")

class StorySegmentBase(BaseModel):
    """Base model for story segments with common validation logic"""
    story_text: str = Field(description="The story text. No more than 15 words THIS IS MANDATORY.  Never mention story beat or radiation level directly. ")
    is_victory: bool = Field(description="Whether this segment ends in Sarah's victory", default=False)
    is_death: bool = Field(description="Whether this segment ends in Sarah's death", default=False)

# Existing response models for story generation steps - preserved for API compatibility
class StoryTextResponse(StorySegmentBase):
    pass

class StoryPromptsResponse(BaseModel):
    image_prompts: List[str] = Field(
        description="List of comic panel descriptions that illustrate the key moments of the scene. Use the word 'Sarah' only when referring to her.",
        min_items=GameConfig.MIN_PANELS,
        max_items=GameConfig.MAX_PANELS
    )

class StoryMetadataResponse(BaseModel):
    radiation_increase: int = Field(
        description=f"How much radiation this segment adds (0-3)",
        ge=0,
        le=3,
        default=GameConfig.DEFAULT_RADIATION_INCREASE
    )
    is_victory: bool = Field(description="Whether this segment ends in Sarah's victory", default=False)
    is_death: bool = Field(description="Whether this segment ends in Sarah's death", default=False)
    choices: List[str] = Field(description="Either empty list for victory/death, or exactly two choices for normal progression")
    time: str = Field(description="Current in-game time in 24h format (HH:MM). Time passes realistically based on actions.")
    location: str = Field(description="Current location.")

    @validator('choices')
    def validate_choices(cls, v, values):
        is_ending = values.get('is_victory', False) or values.get('is_death', False)
        if is_ending:
            if len(v) != 0:
                raise ValueError('For victory/death, choices must be empty')
        else:
            if len(v) != 2:
                raise ValueError('For normal progression, must have exactly 2 choices')
        return v

# Complete story response combining all parts - preserved for API compatibility
class StoryResponse(StorySegmentBase):
    choices: List[Choice]
    raw_choices: List[str] = Field(description="Raw choice texts from LLM before conversion to Choice objects")
    radiation_level: int = Field(description=f"Current radiation level")
    radiation_increase: int = Field(description="How much radiation this segment adds (0-3)", ge=0, le=3, default=GameConfig.DEFAULT_RADIATION_INCREASE)
    time: str = Field(description="Current in-game time in 24h format (HH:MM). Time passes realistically based on actions.")
    location: str = Field(description="Current location.")
    is_first_step: bool = Field(description="Whether this is the first step of the story", default=False)
    image_prompts: List[str] = Field(
        description="List of comic panel descriptions that illustrate the key moments of the scene. Use the word 'Sarah' only when referring to her.",
        min_items=GameConfig.MIN_PANELS,
        max_items=GameConfig.MAX_PANELS
    )

    @validator('choices')
    def validate_choices(cls, v, values):
        is_ending = values.get('is_victory', False) or values.get('is_death', False)
        if is_ending:
            if len(v) != 0:
                raise ValueError('For victory/death, choices must be empty')
        else:
            if len(v) != 2:
                raise ValueError('For normal progression, must have exactly 2 choices')
        return v

# Keep existing models unchanged for compatibility
class ChatMessage(BaseModel):
    message: str
    choice_id: Optional[int] = None

class ImageGenerationRequest(BaseModel):
    prompt: str
    width: int = Field(description="Width of the image to generate")
    height: int = Field(description="Height of the image to generate")

class TextToSpeechRequest(BaseModel):
    text: str
    voice_id: str = "nPczCjzI2devNBz1zQrb"  # Default voice ID (Rachel)