File size: 8,077 Bytes
eab332f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# data_models/tasks.py
from enum import Enum
from typing import List, Optional, Literal
from pydantic import BaseModel, Field, field_validator
from datetime import datetime

# Using Literal for more precise string enums if preferred over Enum class for Pydantic
# However, Enum provides better structure and can be used with Field choices.

class EffortLevel(str, Enum):
    """Estimated effort level for a task."""
    SMALL = "Small"
    MEDIUM = "Medium"
    LARGE = "Large"

class TaskType(str, Enum):
    """Type of task, indicating its nature."""
    INITIATIVE = "initiative"  # Action-oriented, new projects/changes
    TRACKING = "tracking"      # Measurement-focused, monitoring existing metrics/processes

class DataSubject(str, Enum):
    """Specifies the primary data domain a tracking task relates to."""
    FOLLOWER_STATS = "follower_stats"
    POSTS = "posts"
    MENTIONS = "mentions"
    GENERAL = "general" # For initiatives or tasks not tied to a single data type

class TimelineCategory(str, Enum):
    """Categorization of task timelines."""
    IMMEDIATE = "Immediate"       # (e.g., 1-2 weeks)
    SHORT_TERM = "Short-term"     # (e.g., rest of current quarter, up to 3 months)
    MEDIUM_TERM = "Medium-term"   # (e.g., next quarter, 3-6 months)
    LONG_TERM = "Long-term"       # (e.g., 6+ months)

class PriorityLevel(str, Enum):
    """Priority level for tasks."""
    HIGH = "High"
    MEDIUM = "Medium"
    LOW = "Low"

class Task(BaseModel):
    """
    Represents a single actionable task derived from analysis.
    """
    task_category: str = Field(
        description="The broader category or theme of the task (e.g., Content Strategy, Audience Engagement, Reputation Management, Performance Monitoring)."
    )
    task_description: str = Field( # Renamed from 'task' for clarity
        description="A concise yet clear description of the specific action to be taken."
    )
    objective_deliverable: str = Field(
        description="The clear, measurable objective this task aims to achieve and the specific deliverable(s) expected upon completion."
    )
    effort: EffortLevel = Field(
        description="Estimated effort required to complete the task (Small, Medium, Large)."
    )
    timeline: TimelineCategory = Field(
        description="Projected timeline for task completion, considering urgency and dependencies."
    )
    responsible_party: str = Field(
        description="The team, role, or individual suggested to be responsible for executing this task (e.g., Marketing Team, Content Creation Lead, Social Media Manager)."
    )
    success_criteria_metrics: str = Field(
        description="Specific, measurable criteria and metrics that will be used to determine if the task was successfully completed and achieved its objective."
    )
    dependencies_prerequisites: Optional[str] = Field(
        default=None,
        description="Any other tasks, resources, or conditions that must be met before this task can begin or be completed."
    )
    priority: PriorityLevel = Field(
        description="The priority level of the task (High, Medium, Low)."
    )
    priority_justification: str = Field(
        description="A brief explanation for the assigned priority level, linking it to impact or urgency."
    )
    why_proposed: str = Field(
        description="The rationale behind proposing this task, clearly linking it back to specific findings or insights from the data analysis."
    )
    task_type: TaskType = Field(
        description="Indicates whether this task is a new 'initiative' or ongoing 'tracking' of performance/metrics."
    )
    data_subject: Optional[DataSubject] = Field(
        default=None, 
        description="For 'tracking' tasks, specifies the primary data subject (e.g., follower_stats, posts, mentions). Can be 'general' or null for 'initiative' tasks."
    )

    @field_validator('data_subject')
    @classmethod
    def check_data_subject_for_tracking(cls, value: Optional[DataSubject], values) -> Optional[DataSubject]:
        # Pydantic v2 uses `values.data` to get other field values if needed before validation
        # For Pydantic v1, it would be `values.get('task_type')`
        # This example assumes Pydantic v2 structure for `values` if needed, but here we only need `task_type`
        # which should already be validated or available.
        # For simplicity, accessing it via `values.data.get('task_type')` in Pydantic v2 context.
        # If using Pydantic v1, it's `values.get('task_type')`.
        # Let's assume `values` is a dict-like object containing other fields.
        
        # The validator structure depends on Pydantic version.
        # For Pydantic v2, it's `info: ValidationInfo` and `info.data.get('task_type')`
        # For Pydantic v1, `values` is a dict.
        # For this example, let's assume `values` is a dict of the fields.
        task_type_value = None
        if hasattr(values, 'data'): # Pydantic v2 way
             task_type_value = values.data.get('task_type')
        elif isinstance(values, dict): # Pydantic v1 way (or if it's passed as a dict)
             task_type_value = values.get('task_type')


        if task_type_value == TaskType.TRACKING and value is None:
            raise ValueError("For 'tracking' tasks, 'data_subject' must be specified.")
        if task_type_value == TaskType.INITIATIVE and value is DataSubject.GENERAL:
            # This is acceptable, or you might want to enforce it to be None
            pass
        return value

class KeyResult(BaseModel):
    """
    A measurable outcome that contributes to an Objective.
    """
    key_result_description: str = Field( # Renamed from 'key_result'
        description="A clear, specific, and measurable description of the key result."
    )
    tasks: List[Task] = Field(
        default_factory=list,
        description="A list of specific tasks that will be undertaken to achieve this key result."
    )
    target_metric: Optional[str] = Field(
        default=None,
        description="The primary metric used to measure the achievement of this key result (e.g., 'Follower Growth Rate', 'Average Engagement Rate')."
    )
    target_value: Optional[str] = Field( # Can be numeric or descriptive (e.g., "Increase by 10%", "Achieve 5%")
        default=None,
        description="The specific target value for the metric (e.g., '5%', '1000 new followers')."
    )

class OKR(BaseModel):
    """
    Defines an Objective and its associated Key Results (OKRs).
    """
    objective_description: str = Field( # Renamed from 'objective'
        description="A high-level, qualitative goal that the team aims to achieve. Should be aspirational and motivating."
    )
    key_results: List[KeyResult] = Field(
        default_factory=list,
        description="A list of 2-5 specific, measurable, achievable, relevant, and time-bound (SMART) key results that define success for the objective."
    )
    objective_timeline: TimelineCategory = Field(
        description="The overall timeline category for achieving this objective."
    )
    objective_owner: Optional[str] = Field(
        default=None,
        description="The team or individual primarily responsible for this objective."
    )


class TaskExtractionOutput(BaseModel):
    """
    Structured output from the TaskExtractionAgent, including context and OKRs.
    """
    current_quarter_info: str = Field(
        description="Information about the current quarter and days remaining (e.g., 'Q2 2025, 45 days remaining')."
    )
    okrs: List[OKR] = Field(
        default_factory=list,
        description="A list of Objectives and Key Results (OKRs) derived from the analysis."
    )
    overall_strategic_focus: Optional[str] = Field(
        default=None,
        description="A brief summary of the main strategic focus areas identified from the tasks."
    )
    generation_timestamp: str = Field(
        default_factory=lambda: datetime.utcnow().isoformat(),
        description="Timestamp of when this task extraction output was generated."
    )