synkro 0.4.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. synkro/__init__.py +179 -0
  2. synkro/advanced.py +186 -0
  3. synkro/cli.py +128 -0
  4. synkro/core/__init__.py +7 -0
  5. synkro/core/checkpoint.py +250 -0
  6. synkro/core/dataset.py +402 -0
  7. synkro/core/policy.py +337 -0
  8. synkro/errors.py +178 -0
  9. synkro/examples/__init__.py +148 -0
  10. synkro/factory.py +276 -0
  11. synkro/formatters/__init__.py +12 -0
  12. synkro/formatters/qa.py +98 -0
  13. synkro/formatters/sft.py +90 -0
  14. synkro/formatters/tool_call.py +127 -0
  15. synkro/generation/__init__.py +9 -0
  16. synkro/generation/follow_ups.py +134 -0
  17. synkro/generation/generator.py +220 -0
  18. synkro/generation/golden_responses.py +244 -0
  19. synkro/generation/golden_scenarios.py +276 -0
  20. synkro/generation/golden_tool_responses.py +416 -0
  21. synkro/generation/logic_extractor.py +126 -0
  22. synkro/generation/multiturn_responses.py +177 -0
  23. synkro/generation/planner.py +131 -0
  24. synkro/generation/responses.py +189 -0
  25. synkro/generation/scenarios.py +90 -0
  26. synkro/generation/tool_responses.py +376 -0
  27. synkro/generation/tool_simulator.py +114 -0
  28. synkro/interactive/__init__.py +12 -0
  29. synkro/interactive/hitl_session.py +77 -0
  30. synkro/interactive/logic_map_editor.py +173 -0
  31. synkro/interactive/rich_ui.py +205 -0
  32. synkro/llm/__init__.py +7 -0
  33. synkro/llm/client.py +235 -0
  34. synkro/llm/rate_limits.py +95 -0
  35. synkro/models/__init__.py +43 -0
  36. synkro/models/anthropic.py +26 -0
  37. synkro/models/google.py +19 -0
  38. synkro/models/openai.py +31 -0
  39. synkro/modes/__init__.py +15 -0
  40. synkro/modes/config.py +66 -0
  41. synkro/modes/qa.py +18 -0
  42. synkro/modes/sft.py +18 -0
  43. synkro/modes/tool_call.py +18 -0
  44. synkro/parsers.py +442 -0
  45. synkro/pipeline/__init__.py +20 -0
  46. synkro/pipeline/phases.py +592 -0
  47. synkro/pipeline/runner.py +424 -0
  48. synkro/pipelines.py +123 -0
  49. synkro/prompts/__init__.py +57 -0
  50. synkro/prompts/base.py +167 -0
  51. synkro/prompts/golden_templates.py +474 -0
  52. synkro/prompts/interactive_templates.py +65 -0
  53. synkro/prompts/multiturn_templates.py +156 -0
  54. synkro/prompts/qa_templates.py +97 -0
  55. synkro/prompts/templates.py +281 -0
  56. synkro/prompts/tool_templates.py +201 -0
  57. synkro/quality/__init__.py +14 -0
  58. synkro/quality/golden_refiner.py +163 -0
  59. synkro/quality/grader.py +153 -0
  60. synkro/quality/multiturn_grader.py +150 -0
  61. synkro/quality/refiner.py +137 -0
  62. synkro/quality/tool_grader.py +126 -0
  63. synkro/quality/tool_refiner.py +128 -0
  64. synkro/quality/verifier.py +228 -0
  65. synkro/reporting.py +537 -0
  66. synkro/schemas.py +472 -0
  67. synkro/types/__init__.py +41 -0
  68. synkro/types/core.py +126 -0
  69. synkro/types/dataset_type.py +30 -0
  70. synkro/types/logic_map.py +345 -0
  71. synkro/types/tool.py +94 -0
  72. synkro-0.4.12.data/data/examples/__init__.py +148 -0
  73. synkro-0.4.12.dist-info/METADATA +258 -0
  74. synkro-0.4.12.dist-info/RECORD +77 -0
  75. synkro-0.4.12.dist-info/WHEEL +4 -0
  76. synkro-0.4.12.dist-info/entry_points.txt +2 -0
  77. synkro-0.4.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,345 @@
1
+ """Logic Map types for Golden Trace generation.
2
+
3
+ The Logic Map represents a policy as a directed acyclic graph (DAG) of rules,
4
+ enabling grounded reasoning and verification of generated traces.
5
+ """
6
+
7
+ import json
8
+ from enum import Enum
9
+ from pathlib import Path
10
+ from typing import Literal
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+
15
+ class ScenarioType(str, Enum):
16
+ """Types of scenarios for balanced dataset generation."""
17
+
18
+ POSITIVE = "positive" # Happy path - user meets all criteria
19
+ NEGATIVE = "negative" # Violation - user fails one criterion
20
+ EDGE_CASE = "edge_case" # Boundary - user at exact limit
21
+ IRRELEVANT = "irrelevant" # Not covered by policy
22
+
23
+
24
+ class RuleCategory(str, Enum):
25
+ """Categories of rules extracted from policy."""
26
+
27
+ CONSTRAINT = "constraint" # Must/must not conditions
28
+ PERMISSION = "permission" # Allowed/can do
29
+ PROCEDURE = "procedure" # Step-by-step processes
30
+ EXCEPTION = "exception" # Special cases/overrides
31
+
32
+
33
+ class Rule(BaseModel):
34
+ """
35
+ A single rule extracted from the policy document.
36
+
37
+ Rules form nodes in the Logic Map DAG, with dependencies
38
+ indicating which rules must be evaluated first.
39
+
40
+ Examples:
41
+ >>> rule = Rule(
42
+ ... rule_id="R001",
43
+ ... text="Refunds are allowed within 30 days of purchase",
44
+ ... condition="purchase date is within 30 days",
45
+ ... action="allow refund",
46
+ ... dependencies=[],
47
+ ... category=RuleCategory.PERMISSION,
48
+ ... )
49
+ """
50
+
51
+ rule_id: str = Field(
52
+ description="Unique identifier (e.g., 'R001', 'R002')"
53
+ )
54
+ text: str = Field(
55
+ description="Exact rule text from the policy"
56
+ )
57
+ condition: str = Field(
58
+ description="The 'if' part - when this rule applies"
59
+ )
60
+ action: str = Field(
61
+ description="The 'then' part - what happens when rule applies"
62
+ )
63
+ dependencies: list[str] = Field(
64
+ default_factory=list,
65
+ description="Rule IDs that must be evaluated before this rule"
66
+ )
67
+ category: RuleCategory = Field(
68
+ description="Type of rule (constraint, permission, procedure, exception)"
69
+ )
70
+
71
+ def __hash__(self) -> int:
72
+ return hash(self.rule_id)
73
+
74
+ def __eq__(self, other: object) -> bool:
75
+ if not isinstance(other, Rule):
76
+ return False
77
+ return self.rule_id == other.rule_id
78
+
79
+
80
+ class LogicMap(BaseModel):
81
+ """
82
+ Directed Acyclic Graph (DAG) of rules extracted from a policy.
83
+
84
+ The Logic Map is the "Map of Truth" that enables:
85
+ - Grounded scenario generation with rule references
86
+ - Chain-of-thought reasoning with rule citations
87
+ - Verification that traces don't skip or hallucinate rules
88
+
89
+ Examples:
90
+ >>> logic_map = LogicMap(
91
+ ... rules=[rule1, rule2, rule3],
92
+ ... root_rules=["R001"], # Entry points
93
+ ... )
94
+ >>> print(logic_map.get_rule("R001"))
95
+ """
96
+
97
+ rules: list[Rule] = Field(
98
+ description="All rules extracted from the policy"
99
+ )
100
+ root_rules: list[str] = Field(
101
+ default_factory=list,
102
+ description="Rule IDs with no dependencies (entry points)"
103
+ )
104
+
105
+ def get_rule(self, rule_id: str) -> Rule | None:
106
+ """Get a rule by its ID."""
107
+ for rule in self.rules:
108
+ if rule.rule_id == rule_id:
109
+ return rule
110
+ return None
111
+
112
+ def get_dependents(self, rule_id: str) -> list[Rule]:
113
+ """Get all rules that depend on the given rule."""
114
+ return [r for r in self.rules if rule_id in r.dependencies]
115
+
116
+ def get_dependencies(self, rule_id: str) -> list[Rule]:
117
+ """Get all rules that the given rule depends on."""
118
+ rule = self.get_rule(rule_id)
119
+ if not rule:
120
+ return []
121
+ return [r for r in self.rules if r.rule_id in rule.dependencies]
122
+
123
+ def get_chain(self, rule_id: str) -> list[Rule]:
124
+ """
125
+ Get the full dependency chain for a rule (topologically sorted).
126
+
127
+ Returns all rules that must be evaluated before the given rule,
128
+ in the order they should be evaluated.
129
+ """
130
+ visited = set()
131
+ chain = []
132
+
133
+ def visit(rid: str):
134
+ if rid in visited:
135
+ return
136
+ visited.add(rid)
137
+ rule = self.get_rule(rid)
138
+ if rule:
139
+ for dep_id in rule.dependencies:
140
+ visit(dep_id)
141
+ chain.append(rule)
142
+
143
+ visit(rule_id)
144
+ return chain
145
+
146
+ def validate_dag(self) -> bool:
147
+ """Verify the rules form a valid DAG (no cycles)."""
148
+ # Track visit state: 0=unvisited, 1=visiting, 2=visited
149
+ state = {r.rule_id: 0 for r in self.rules}
150
+
151
+ def has_cycle(rule_id: str) -> bool:
152
+ if state.get(rule_id, 0) == 1: # Currently visiting = cycle
153
+ return True
154
+ if state.get(rule_id, 0) == 2: # Already visited = ok
155
+ return False
156
+
157
+ state[rule_id] = 1 # Mark as visiting
158
+ rule = self.get_rule(rule_id)
159
+ if rule:
160
+ for dep_id in rule.dependencies:
161
+ if has_cycle(dep_id):
162
+ return True
163
+ state[rule_id] = 2 # Mark as visited
164
+ return False
165
+
166
+ for rule in self.rules:
167
+ if has_cycle(rule.rule_id):
168
+ return False
169
+ return True
170
+
171
+ def get_rules_by_category(self, category: RuleCategory) -> list[Rule]:
172
+ """Get all rules of a specific category."""
173
+ return [r for r in self.rules if r.category == category]
174
+
175
+ def to_display_string(self) -> str:
176
+ """Generate a human-readable representation of the Logic Map."""
177
+ lines = [f"Logic Map ({len(self.rules)} rules)"]
178
+ lines.append("=" * 40)
179
+
180
+ # Show root rules first
181
+ lines.append("\nRoot Rules (Entry Points):")
182
+ for rid in self.root_rules:
183
+ rule = self.get_rule(rid)
184
+ if rule:
185
+ lines.append(f" {rid}: {rule.text[:60]}...")
186
+
187
+ # Show dependency chains
188
+ lines.append("\nDependency Chains:")
189
+ processed = set()
190
+ for rule in self.rules:
191
+ if rule.rule_id not in processed and rule.dependencies:
192
+ chain = " -> ".join(r.rule_id for r in self.get_chain(rule.rule_id))
193
+ lines.append(f" {chain}")
194
+ processed.update(r.rule_id for r in self.get_chain(rule.rule_id))
195
+
196
+ return "\n".join(lines)
197
+
198
+ def save(self, path: str | Path) -> None:
199
+ """
200
+ Save the Logic Map to a JSON file.
201
+
202
+ Args:
203
+ path: File path to save to (e.g., "logic_map.json")
204
+
205
+ Examples:
206
+ >>> logic_map.save("logic_map.json")
207
+ >>> # Later, reload it
208
+ >>> logic_map = LogicMap.load("logic_map.json")
209
+ """
210
+ path = Path(path)
211
+ with open(path, "w") as f:
212
+ json.dump(self.model_dump(), f, indent=2)
213
+
214
+ @classmethod
215
+ def load(cls, path: str | Path) -> "LogicMap":
216
+ """
217
+ Load a Logic Map from a JSON file.
218
+
219
+ Args:
220
+ path: File path to load from
221
+
222
+ Returns:
223
+ LogicMap instance
224
+
225
+ Examples:
226
+ >>> logic_map = LogicMap.load("logic_map.json")
227
+ >>> print(f"Loaded {len(logic_map.rules)} rules")
228
+ """
229
+ path = Path(path)
230
+ with open(path) as f:
231
+ data = json.load(f)
232
+ return cls.model_validate(data)
233
+
234
+
235
+ class ReasoningStep(BaseModel):
236
+ """
237
+ A single step in the Chain-of-Thought reasoning.
238
+
239
+ Each step references exactly one rule and explains how it applies
240
+ (or doesn't apply) to the current scenario.
241
+ """
242
+
243
+ rule_id: str = Field(
244
+ description="The rule being evaluated in this step"
245
+ )
246
+ rule_text: str = Field(
247
+ description="The text of the rule"
248
+ )
249
+ applies: bool = Field(
250
+ description="Whether this rule applies to the scenario"
251
+ )
252
+ reasoning: str = Field(
253
+ description="Explanation of why the rule does/doesn't apply"
254
+ )
255
+ exclusions: list[str] = Field(
256
+ default_factory=list,
257
+ description="Rule IDs that are excluded because this rule applies"
258
+ )
259
+
260
+
261
+ class GoldenScenario(BaseModel):
262
+ """
263
+ A scenario with explicit type and rule targeting.
264
+
265
+ Extends the base Scenario concept with:
266
+ - Explicit scenario type (positive, negative, edge_case, irrelevant)
267
+ - Target rule IDs that this scenario is designed to test
268
+ - Expected outcome based on the rules
269
+ """
270
+
271
+ description: str = Field(
272
+ description="The user's request or question"
273
+ )
274
+ context: str = Field(
275
+ default="",
276
+ description="Additional context for the scenario"
277
+ )
278
+ category: str = Field(
279
+ default="",
280
+ description="The policy category this scenario belongs to"
281
+ )
282
+ scenario_type: ScenarioType = Field(
283
+ description="Type of scenario (positive, negative, edge_case, irrelevant)"
284
+ )
285
+ target_rule_ids: list[str] = Field(
286
+ default_factory=list,
287
+ description="Rule IDs this scenario is designed to test"
288
+ )
289
+ expected_outcome: str = Field(
290
+ default="",
291
+ description="Expected response behavior based on rules"
292
+ )
293
+
294
+ def to_base_scenario(self) -> "Scenario":
295
+ """Convert to base Scenario type for compatibility."""
296
+ from synkro.types.core import Scenario
297
+ return Scenario(
298
+ description=self.description,
299
+ context=self.context,
300
+ category=self.category,
301
+ )
302
+
303
+
304
+ class VerificationResult(BaseModel):
305
+ """
306
+ Result of verifying a trace against the Logic Map.
307
+
308
+ The Auditor produces this to indicate whether a trace
309
+ correctly applies all relevant rules without hallucination.
310
+ """
311
+
312
+ passed: bool = Field(
313
+ description="Whether the trace passed verification"
314
+ )
315
+ issues: list[str] = Field(
316
+ default_factory=list,
317
+ description="List of issues found (if any)"
318
+ )
319
+ skipped_rules: list[str] = Field(
320
+ default_factory=list,
321
+ description="Rule IDs that should have been applied but weren't"
322
+ )
323
+ hallucinated_rules: list[str] = Field(
324
+ default_factory=list,
325
+ description="Rule IDs cited that don't exist or don't apply"
326
+ )
327
+ contradictions: list[str] = Field(
328
+ default_factory=list,
329
+ description="Logical contradictions found in the trace"
330
+ )
331
+ rules_verified: list[str] = Field(
332
+ default_factory=list,
333
+ description="Rule IDs that were correctly applied"
334
+ )
335
+
336
+
337
+ __all__ = [
338
+ "ScenarioType",
339
+ "RuleCategory",
340
+ "Rule",
341
+ "LogicMap",
342
+ "ReasoningStep",
343
+ "GoldenScenario",
344
+ "VerificationResult",
345
+ ]
synkro/types/tool.py ADDED
@@ -0,0 +1,94 @@
1
+ """Tool-related types for tool call trace generation."""
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class ToolFunction(BaseModel):
7
+ """Function details within a tool call."""
8
+
9
+ name: str = Field(description="Name of the function to call")
10
+ arguments: str = Field(description="JSON string of function arguments")
11
+
12
+
13
+ class ToolCall(BaseModel):
14
+ """A tool call made by the assistant."""
15
+
16
+ id: str = Field(description="Unique identifier for this tool call")
17
+ type: str = Field(default="function", description="Type of tool call")
18
+ function: ToolFunction = Field(description="Function details")
19
+
20
+
21
+ class ToolResult(BaseModel):
22
+ """Result from a tool execution."""
23
+
24
+ tool_call_id: str = Field(description="ID of the tool call this responds to")
25
+ content: str = Field(description="The tool's response content")
26
+
27
+
28
+ class ToolDefinition(BaseModel):
29
+ """
30
+ Definition of a tool that an agent can use.
31
+
32
+ Examples:
33
+ >>> web_search = ToolDefinition(
34
+ ... name="web_search",
35
+ ... description="Search the web for current information",
36
+ ... parameters={
37
+ ... "type": "object",
38
+ ... "properties": {
39
+ ... "query": {"type": "string", "description": "Search query"}
40
+ ... },
41
+ ... "required": ["query"]
42
+ ... },
43
+ ... examples=[{"query": "weather in NYC"}],
44
+ ... mock_responses=["NYC: 72°F, sunny"]
45
+ ... )
46
+ """
47
+
48
+ name: str = Field(description="Name of the tool")
49
+ description: str = Field(description="What the tool does")
50
+ parameters: dict = Field(
51
+ description="JSON Schema for the tool's parameters",
52
+ default_factory=lambda: {"type": "object", "properties": {}}
53
+ )
54
+ examples: list[dict] = Field(
55
+ default_factory=list,
56
+ description="Example tool calls for few-shot learning"
57
+ )
58
+ mock_responses: list[str] = Field(
59
+ default_factory=list,
60
+ description="Example responses for simulation"
61
+ )
62
+
63
+ def to_openai_format(self) -> dict:
64
+ """Convert to OpenAI function calling format."""
65
+ return {
66
+ "type": "function",
67
+ "function": {
68
+ "name": self.name,
69
+ "description": self.description,
70
+ "parameters": self.parameters,
71
+ }
72
+ }
73
+
74
+ def to_system_prompt(self) -> str:
75
+ """Generate a system prompt description of this tool."""
76
+ params_desc = []
77
+ props = self.parameters.get("properties", {})
78
+ required = self.parameters.get("required", [])
79
+
80
+ for param_name, param_info in props.items():
81
+ param_type = param_info.get("type", "any")
82
+ param_desc = param_info.get("description", "")
83
+ req_marker = " (required)" if param_name in required else ""
84
+ params_desc.append(f" - {param_name}: {param_type}{req_marker} - {param_desc}")
85
+
86
+ params_str = "\n".join(params_desc) if params_desc else " (no parameters)"
87
+
88
+ return f"""**{self.name}**: {self.description}
89
+ Parameters:
90
+ {params_str}"""
91
+
92
+
93
+ __all__ = ["ToolDefinition", "ToolCall", "ToolFunction", "ToolResult"]
94
+
@@ -0,0 +1,148 @@
1
+ """Built-in example policies for instant demos."""
2
+
3
+ EXPENSE_POLICY = """# Company Expense Policy
4
+
5
+ ## Approval Thresholds
6
+ - Expenses under $50: No approval required
7
+ - Expenses $50-$500: Manager approval required
8
+ - Expenses over $500: VP approval required
9
+
10
+ ## Receipt Requirements
11
+ - All expenses over $25 must have a receipt
12
+ - Digital receipts are acceptable
13
+ - Missing receipts require written justification within 48 hours
14
+
15
+ ## Categories
16
+ - Travel: Flights, hotels, ground transportation, meals while traveling
17
+ - Meals: Client meals, team events (max $75/person)
18
+ - Software: Must be on pre-approved list, exceptions need IT approval
19
+ - Equipment: Must be on asset tracking list if over $200
20
+ - Office Supplies: Under $100 can be purchased directly
21
+
22
+ ## Reimbursement Timeline
23
+ - Submit expenses within 30 days of purchase
24
+ - Reimbursements processed within 14 business days
25
+ - Late submissions require manager exception approval
26
+ """
27
+
28
+ HR_HANDBOOK = """# Employee Handbook
29
+
30
+ ## Work Hours
31
+ - Standard work week is 40 hours, Monday through Friday
32
+ - Core hours are 10am to 3pm when all employees should be available
33
+ - Flexible scheduling allowed with manager approval
34
+
35
+ ## Time Off
36
+ - Full-time employees receive 15 days PTO per year
37
+ - PTO accrues monthly (1.25 days per month)
38
+ - Unused PTO can roll over up to 5 days
39
+ - PTO requests must be submitted 2 weeks in advance for 3+ days
40
+
41
+ ## Remote Work
42
+ - Hybrid schedule: minimum 2 days in office per week
43
+ - Fully remote requires director approval
44
+ - Home office stipend of $500 for remote workers
45
+
46
+ ## Performance Reviews
47
+ - Annual reviews conducted in December
48
+ - Mid-year check-ins in June
49
+ - Goals set at start of fiscal year
50
+ - Promotions considered during annual review cycle only
51
+ """
52
+
53
+ REFUND_POLICY = """# Return and Refund Policy
54
+
55
+ ## Eligibility
56
+ - Items can be returned within 30 days of purchase
57
+ - Items must be unused and in original packaging
58
+ - Receipt or proof of purchase required
59
+
60
+ ## Exceptions
61
+ - Final sale items cannot be returned
62
+ - Personalized items cannot be returned
63
+ - Perishable goods cannot be returned after 7 days
64
+
65
+ ## Refund Process
66
+ - Refunds issued to original payment method
67
+ - Processing takes 5-10 business days
68
+ - Shipping costs are non-refundable unless item was defective
69
+
70
+ ## Exchanges
71
+ - Exchanges available within 30 days
72
+ - Size exchanges free of charge
73
+ - Different item exchanges treated as return + new purchase
74
+
75
+ ## Defective Items
76
+ - Report defects within 14 days
77
+ - Photos required for defect claims
78
+ - Replacement or full refund offered for confirmed defects
79
+ """
80
+
81
+ SUPPORT_GUIDELINES = """# Customer Support Guidelines
82
+
83
+ ## Response Times
84
+ - Chat: Respond within 2 minutes
85
+ - Email: Respond within 4 hours during business hours
86
+ - Phone: Answer within 30 seconds, max hold time 3 minutes
87
+
88
+ ## Escalation Tiers
89
+ - Tier 1: General questions, password resets, basic troubleshooting
90
+ - Tier 2: Technical issues, billing disputes, account problems
91
+ - Tier 3: Complex technical issues, executive escalations
92
+
93
+ ## Refund Authority
94
+ - Tier 1 can issue refunds up to $50
95
+ - Tier 2 can issue refunds up to $200
96
+ - Tier 3 or manager approval needed for refunds over $200
97
+
98
+ ## Documentation
99
+ - Log all customer interactions in CRM
100
+ - Include customer sentiment and issue category
101
+ - Note any promised follow-ups with deadlines
102
+ """
103
+
104
+ SECURITY_POLICY = """# Information Security Policy
105
+
106
+ ## Password Requirements
107
+ - Minimum 12 characters
108
+ - Must include uppercase, lowercase, number, and symbol
109
+ - Change every 90 days
110
+ - Cannot reuse last 10 passwords
111
+
112
+ ## Access Control
113
+ - Principle of least privilege applies
114
+ - Access requests require manager approval
115
+ - Quarterly access reviews mandatory
116
+ - Terminate access within 24 hours of employee departure
117
+
118
+ ## Data Classification
119
+ - Public: Marketing materials, job postings
120
+ - Internal: Company announcements, policies
121
+ - Confidential: Customer data, financials
122
+ - Restricted: PII, payment info, credentials
123
+
124
+ ## Incident Response
125
+ - Report security incidents within 1 hour
126
+ - Do not attempt to investigate independently
127
+ - Preserve evidence (don't delete logs or files)
128
+ - Security team leads all incident response
129
+ """
130
+
131
+ # All policies available as a list
132
+ ALL_POLICIES = [
133
+ ("expense", EXPENSE_POLICY),
134
+ ("hr", HR_HANDBOOK),
135
+ ("refund", REFUND_POLICY),
136
+ ("support", SUPPORT_GUIDELINES),
137
+ ("security", SECURITY_POLICY),
138
+ ]
139
+
140
+ __all__ = [
141
+ "EXPENSE_POLICY",
142
+ "HR_HANDBOOK",
143
+ "REFUND_POLICY",
144
+ "SUPPORT_GUIDELINES",
145
+ "SECURITY_POLICY",
146
+ "ALL_POLICIES",
147
+ ]
148
+