File size: 7,941 Bytes
d075a5b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
"""
Error-prone skills API variants for testing agent resilience.

Each function has a unique, plausible intent and embeds a specific error behavior.
Completely independent from original APIs β€” accesses DataLoader directly.

AUTO-GENERATED by scripts/generate_hf.sh - DO NOT EDIT DIRECTLY
Edit skills_error.py in main repo and regenerate.
"""

import json
import random
from typing import Any, Dict, Optional

from data_loader import get_data_loader

# Seeded RNG for reproducible probabilistic behavior
_rng = random.Random(42)


def _check_requisition(requisition_id: str) -> Optional[Dict[str, Any]]:
    """Return error dict if requisition invalid, else None."""
    loader = get_data_loader()
    if not loader.is_valid_requisition(requisition_id):
        return {
            "error": "requisition_not_found",
            "message": f"Requisition {requisition_id} not found",
        }
    return None


# ── Test 27: Type mismatch β€” string instead of structured list ──────────────

def get_skill_summary(requisition_id: str) -> str:
    """Get a quick text summary of skills needed for a requisition.

    Returns a concise comma-separated skill overview.

    ERROR BEHAVIOR: Returns a plain comma-separated string instead of
    structured SkillAnalysisResponse. Tests type mismatch handling.
    """
    err = _check_requisition(requisition_id)
    if err:
        return json.dumps(err)

    loader = get_data_loader()
    data = loader.get_similar_requisitions(requisition_id)
    all_skills: set = set()
    for skills_list in data["skills_parsed"].dropna():
        if isinstance(skills_list, list):
            all_skills.update(skills_list)

    return ", ".join(sorted(all_skills))


# ── Test 34: Missing output schema β€” untyped dict ───────────────────────────

def get_model_registry(requisition_id: str) -> Dict[str, Any]:
    """Check which ML models are registered for a given requisition.

    Returns model registry information including versions and status.

    ERROR BEHAVIOR: No Pydantic output schema β€” returns a plain dict
    with dynamically typed fields. Tests schema inference.
    """
    err = _check_requisition(requisition_id)
    if err:
        return err

    return {
        "requisition_id": requisition_id,
        "models": [
            {
                "name": "Skill relevance classifier",
                "version": "2.1.0",
                "status": "active",
                "last_trained": "2024-11-15",
                "accuracy": 0.87,
            },
            {
                "name": "SLA impact regression model",
                "version": "1.4.2",
                "status": "active",
                "last_trained": "2024-10-01",
                "r_squared": 0.72,
            },
            {
                "name": "Funnel conversion recommender",
                "version": "3.0.0-beta",
                "status": "staging",
                "last_trained": "2025-01-20",
                "precision": 0.81,
            },
        ],
        "registry_updated": "2025-04-29",
    }


# ── Test 35: Missing input schema β€” undocumented params ─────────────────────

def get_skill_lookup(requisition_id: str, skill_name: str = None,
                     include_history: bool = False,
                     format: str = "json") -> Dict[str, Any]:
    """Look up a specific skill and its metrics for a requisition.

    ERROR BEHAVIOR: Accepts undocumented parameters (include_history, format)
    not described in the tool schema. Tests agent handling of extra params.
    """
    err = _check_requisition(requisition_id)
    if err:
        return err

    loader = get_data_loader()
    data = loader.get_similar_requisitions(requisition_id)

    # Find skill occurrence
    total = 0
    for skills_list in data["skills_parsed"].dropna():
        if isinstance(skills_list, list) and skill_name in skills_list:
            total += 1

    result = {
        "requisition_id": requisition_id,
        "skill_name": skill_name,
        "occurrence_count": total,
        "total_candidates": len(data),
        "occurrence_rate": round(total / len(data) * 100, 1) if len(data) > 0 else 0,
    }

    if include_history:
        result["history"] = {
            "first_seen": "2023-10-09",
            "trend": "stable",
            "quarterly_counts": [total // 4] * 4,
        }

    return result


# ── Test 40: Deeply nested JSON (15 levels) ─────────────────────────────────

def get_skill_deep_analysis(requisition_id: str) -> Dict[str, Any]:
    """Get a deep analysis breakdown of skills with detailed sub-categories.

    Returns comprehensive multi-level skill categorization and metrics.

    ERROR BEHAVIOR: Response is nested 15 levels deep.
    Tests agent ability to navigate deeply nested structures.
    """
    err = _check_requisition(requisition_id)
    if err:
        return err

    loader = get_data_loader()
    data = loader.get_similar_requisitions(requisition_id)

    # Collect top skills
    all_skills: list = []
    for skills_list in data["skills_parsed"].dropna():
        if isinstance(skills_list, list):
            all_skills.extend(skills_list)

    from collections import Counter
    skill_counts = Counter(all_skills)
    top_skills = skill_counts.most_common(5)

    # Build deeply nested structure (15 levels)
    def nest(depth: int, skill_name: str, count: int) -> Dict[str, Any]:
        if depth <= 0:
            return {"skill": skill_name, "count": count}
        return {
            "level": depth,
            "metadata": {"type": f"analysis_layer_{depth}"},
            "data": nest(depth - 1, skill_name, count),
        }

    skills_nested = [
        nest(15, name, count) for name, count in top_skills
    ]

    return {
        "requisition_id": requisition_id,
        "analysis_version": "3.0",
        "results": {
            "nested_skills": skills_nested,
            "total_depth": 15,
        },
    }


# ── Test 42: Input schema mismatch β€” expects skill_id but docs say skill_name

def analyze_skill_match(requisition_id: str, skill_id: str) -> Dict[str, Any]:
    """Check if a skill is a good match for a requisition.

    Args:
        requisition_id: The job requisition ID.
        skill_id: The skill identifier to check.

    ERROR BEHAVIOR: Function signature says `skill_id` but tool description
    and documentation say `skill_name`. Tests agent adaptation to mismatched
    parameter names.
    """
    err = _check_requisition(requisition_id)
    if err:
        return err

    # Treat skill_id as skill_name (the mismatch)
    skill_name = skill_id

    loader = get_data_loader()
    data = loader.get_similar_requisitions(requisition_id)
    reviewed = data[data["reviewed"]]

    has_skill = reviewed[reviewed["skills_parsed"].apply(lambda x: skill_name in x)]
    no_skill = reviewed[reviewed["skills_parsed"].apply(lambda x: skill_name not in x)]

    sla_with = round(has_skill["sla_met"].mean() * 100) if len(has_skill) > 0 else 0
    sla_without = round(no_skill["sla_met"].mean() * 100) if len(no_skill) > 0 else 0

    total_with_skill = sum(
        1 for sl in data["skills_parsed"].dropna()
        if isinstance(sl, list) and skill_name in sl
    )

    match_score = min(100, int(
        (total_with_skill / len(data) * 50 if len(data) > 0 else 0)
        + (max(0, sla_with - sla_without))
    ))

    return {
        "requisition_id": requisition_id,
        "skill_id": skill_name,
        "match_score": match_score,
        "sla_delta": sla_with - sla_without,
        "occurrence_rate": round(total_with_skill / len(data) * 100, 1) if len(data) > 0 else 0,
        "recommendation": "good match" if match_score >= 50 else "weak match",
    }