synkro 0.4.5__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.
Potentially problematic release.
This version of synkro might be problematic. Click here for more details.
- synkro/__init__.py +165 -0
- synkro/cli.py +120 -0
- synkro/core/__init__.py +7 -0
- synkro/core/dataset.py +233 -0
- synkro/core/policy.py +337 -0
- synkro/errors.py +178 -0
- synkro/examples/__init__.py +148 -0
- synkro/factory.py +160 -0
- synkro/formatters/__init__.py +12 -0
- synkro/formatters/qa.py +85 -0
- synkro/formatters/sft.py +90 -0
- synkro/formatters/tool_call.py +127 -0
- synkro/generation/__init__.py +9 -0
- synkro/generation/generator.py +163 -0
- synkro/generation/planner.py +87 -0
- synkro/generation/responses.py +160 -0
- synkro/generation/scenarios.py +90 -0
- synkro/generation/tool_responses.py +370 -0
- synkro/generation/tool_simulator.py +114 -0
- synkro/llm/__init__.py +7 -0
- synkro/llm/client.py +235 -0
- synkro/llm/rate_limits.py +95 -0
- synkro/models/__init__.py +43 -0
- synkro/models/anthropic.py +26 -0
- synkro/models/google.py +19 -0
- synkro/models/openai.py +31 -0
- synkro/modes/__init__.py +15 -0
- synkro/modes/config.py +66 -0
- synkro/modes/qa.py +18 -0
- synkro/modes/sft.py +18 -0
- synkro/modes/tool_call.py +18 -0
- synkro/parsers.py +442 -0
- synkro/pipeline/__init__.py +20 -0
- synkro/pipeline/phases.py +237 -0
- synkro/pipeline/runner.py +198 -0
- synkro/pipelines.py +105 -0
- synkro/prompts/__init__.py +44 -0
- synkro/prompts/base.py +167 -0
- synkro/prompts/qa_templates.py +97 -0
- synkro/prompts/templates.py +281 -0
- synkro/prompts/tool_templates.py +201 -0
- synkro/quality/__init__.py +14 -0
- synkro/quality/grader.py +130 -0
- synkro/quality/refiner.py +137 -0
- synkro/quality/tool_grader.py +126 -0
- synkro/quality/tool_refiner.py +128 -0
- synkro/reporting.py +213 -0
- synkro/schemas.py +325 -0
- synkro/types/__init__.py +41 -0
- synkro/types/core.py +113 -0
- synkro/types/dataset_type.py +30 -0
- synkro/types/tool.py +94 -0
- synkro-0.4.5.data/data/examples/__init__.py +148 -0
- synkro-0.4.5.dist-info/METADATA +221 -0
- synkro-0.4.5.dist-info/RECORD +58 -0
- synkro-0.4.5.dist-info/WHEEL +4 -0
- synkro-0.4.5.dist-info/entry_points.txt +2 -0
- synkro-0.4.5.dist-info/licenses/LICENSE +21 -0
synkro/types/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Type definitions for Synkro.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
from synkro.types import DatasetType, Message, Trace
|
|
5
|
+
from synkro.types import ToolDefinition, ToolCall, ToolFunction
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from synkro.types.core import (
|
|
9
|
+
Role,
|
|
10
|
+
Message,
|
|
11
|
+
Scenario,
|
|
12
|
+
Trace,
|
|
13
|
+
GradeResult,
|
|
14
|
+
Plan,
|
|
15
|
+
Category,
|
|
16
|
+
)
|
|
17
|
+
from synkro.types.dataset_type import DatasetType
|
|
18
|
+
from synkro.types.tool import (
|
|
19
|
+
ToolDefinition,
|
|
20
|
+
ToolCall,
|
|
21
|
+
ToolFunction,
|
|
22
|
+
ToolResult,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Dataset type
|
|
27
|
+
"DatasetType",
|
|
28
|
+
# Core types
|
|
29
|
+
"Role",
|
|
30
|
+
"Message",
|
|
31
|
+
"Scenario",
|
|
32
|
+
"Trace",
|
|
33
|
+
"GradeResult",
|
|
34
|
+
"Plan",
|
|
35
|
+
"Category",
|
|
36
|
+
# Tool types
|
|
37
|
+
"ToolDefinition",
|
|
38
|
+
"ToolCall",
|
|
39
|
+
"ToolFunction",
|
|
40
|
+
"ToolResult",
|
|
41
|
+
]
|
synkro/types/core.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Core Pydantic models for Synkro."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Any
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Role = Literal["system", "user", "assistant", "tool"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Message(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
A single message in a conversation.
|
|
13
|
+
|
|
14
|
+
Supports both regular chat messages and tool-calling messages.
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
>>> # Regular message
|
|
18
|
+
>>> Message(role="user", content="Hello")
|
|
19
|
+
|
|
20
|
+
>>> # Assistant with tool call (tool_calls is list of dicts or ToolCall objects)
|
|
21
|
+
>>> Message(role="assistant", content=None, tool_calls=[...])
|
|
22
|
+
|
|
23
|
+
>>> # Tool response
|
|
24
|
+
>>> Message(role="tool", content="Result", tool_call_id="call_123")
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
role: Role
|
|
28
|
+
content: str | None = None
|
|
29
|
+
tool_calls: list[Any] | None = Field(
|
|
30
|
+
default=None,
|
|
31
|
+
description="Tool calls made by the assistant (list of ToolCall or dicts)"
|
|
32
|
+
)
|
|
33
|
+
tool_call_id: str | None = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description="ID of the tool call this message responds to (for tool role)"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def model_post_init(self, __context) -> None:
|
|
39
|
+
"""Validate message structure based on role."""
|
|
40
|
+
# For backwards compatibility, ensure content is string for non-tool roles
|
|
41
|
+
if self.role in ("system", "user") and self.content is None:
|
|
42
|
+
self.content = ""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Scenario(BaseModel):
|
|
46
|
+
"""A test scenario for trace generation."""
|
|
47
|
+
|
|
48
|
+
description: str = Field(description="The scenario description")
|
|
49
|
+
context: str = Field(description="Additional context and background")
|
|
50
|
+
category: str | None = Field(default=None, description="Category this scenario belongs to")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class GradeResult(BaseModel):
|
|
54
|
+
"""Result of grading a trace."""
|
|
55
|
+
|
|
56
|
+
passed: bool = Field(description="Whether the trace passes quality checks")
|
|
57
|
+
issues: list[str] = Field(default_factory=list, description="List of issues found")
|
|
58
|
+
feedback: str = Field(default="", description="Summary feedback for improvement")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Trace(BaseModel):
|
|
62
|
+
"""A complete training trace with messages and metadata."""
|
|
63
|
+
|
|
64
|
+
messages: list[Message] = Field(description="The conversation messages")
|
|
65
|
+
scenario: Scenario = Field(description="The scenario this trace was generated from")
|
|
66
|
+
grade: GradeResult | None = Field(default=None, description="Grading result if graded")
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def system_message(self) -> str | None:
|
|
70
|
+
"""Get the system message content."""
|
|
71
|
+
for m in self.messages:
|
|
72
|
+
if m.role == "system":
|
|
73
|
+
return m.content
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def user_message(self) -> str:
|
|
78
|
+
"""Get the first user message content."""
|
|
79
|
+
for m in self.messages:
|
|
80
|
+
if m.role == "user":
|
|
81
|
+
return m.content or ""
|
|
82
|
+
return ""
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def assistant_message(self) -> str:
|
|
86
|
+
"""Get the last assistant message content."""
|
|
87
|
+
for m in reversed(self.messages):
|
|
88
|
+
if m.role == "assistant":
|
|
89
|
+
return m.content or ""
|
|
90
|
+
return ""
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def has_tool_calls(self) -> bool:
|
|
94
|
+
"""Check if this trace contains any tool calls."""
|
|
95
|
+
for m in self.messages:
|
|
96
|
+
if m.tool_calls:
|
|
97
|
+
return True
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Category(BaseModel):
|
|
102
|
+
"""A category for organizing scenarios."""
|
|
103
|
+
|
|
104
|
+
name: str = Field(description="Category name")
|
|
105
|
+
description: str = Field(description="What this category tests")
|
|
106
|
+
count: int = Field(description="Number of traces to generate for this category")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class Plan(BaseModel):
|
|
110
|
+
"""A generation plan with categories."""
|
|
111
|
+
|
|
112
|
+
categories: list[Category] = Field(description="Categories with trace allocations")
|
|
113
|
+
reasoning: str = Field(description="Explanation of why these categories were chosen")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Dataset type enum for steering generation pipeline."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DatasetType(str, Enum):
|
|
7
|
+
"""
|
|
8
|
+
Type of dataset to generate.
|
|
9
|
+
|
|
10
|
+
The dataset type determines:
|
|
11
|
+
- Prompts used for scenario and response generation
|
|
12
|
+
- Grading criteria
|
|
13
|
+
- Output format and schema
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
>>> from synkro import DatasetType
|
|
17
|
+
>>> synkro.generate(policy, dataset_type=DatasetType.QA)
|
|
18
|
+
>>> synkro.generate(policy, dataset_type=DatasetType.SFT)
|
|
19
|
+
>>> synkro.generate(policy, dataset_type=DatasetType.TOOL_CALL, tools=[...])
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
QA = "qa"
|
|
23
|
+
"""Question-Answer pairs: {question, answer, context}"""
|
|
24
|
+
|
|
25
|
+
SFT = "sft"
|
|
26
|
+
"""Supervised Fine-Tuning: {messages: [system, user, assistant]}"""
|
|
27
|
+
|
|
28
|
+
TOOL_CALL = "tool_call"
|
|
29
|
+
"""Tool Calling: {messages: [..., {tool_calls: [...]}, {role: tool}, ...]}"""
|
|
30
|
+
|
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
|
+
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: synkro
|
|
3
|
+
Version: 0.4.5
|
|
4
|
+
Summary: Generate training datasets from any document
|
|
5
|
+
Author: Murtaza Meerza
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: dataset-generation,fine-tuning,llm,synthetic-data,training-data
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: beautifulsoup4>=4.12
|
|
19
|
+
Requires-Dist: html2text>=2020.1
|
|
20
|
+
Requires-Dist: httpx>=0.25
|
|
21
|
+
Requires-Dist: litellm>=1.40
|
|
22
|
+
Requires-Dist: mammoth>=1.6
|
|
23
|
+
Requires-Dist: marker-pdf>=0.2
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
Requires-Dist: python-dotenv>=1.0
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Requires-Dist: typer>=0.9
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Synkro
|
|
35
|
+
|
|
36
|
+
Turn policies, handbooks, and documentation into high-quality training data for fine-tuning LLMs.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Quality Evaluation** - Each response is graded and automatically refined if it fails
|
|
41
|
+
- **Multiple Formats** - SFT (chat), QA (question-answer), and Tool Calling
|
|
42
|
+
- **Tool Call Training** - Generate OpenAI function calling format for teaching models to use custom tools
|
|
43
|
+
- **Top LLM Providers** - OpenAI, Anthropic, and Google
|
|
44
|
+
- **File Support** - PDF, DOCX, TXT, Markdown, URLs
|
|
45
|
+
- **CLI Included** - Generate datasets from the command line
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install synkro
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from synkro.pipelines import create_pipeline
|
|
57
|
+
from synkro.models.google import Google
|
|
58
|
+
from synkro.types import DatasetType
|
|
59
|
+
|
|
60
|
+
pipeline = create_pipeline(
|
|
61
|
+
model=Google.GEMINI_25_FLASH, # Fast generation
|
|
62
|
+
grading_model=Google.GEMINI_25_PRO, # Quality grading
|
|
63
|
+
dataset_type=DatasetType.SFT,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
dataset = pipeline.generate(
|
|
67
|
+
"All expenses over $50 require manager approval.",
|
|
68
|
+
traces=50,
|
|
69
|
+
)
|
|
70
|
+
dataset.save("training.jsonl")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### From Files
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from synkro.pipelines import create_pipeline
|
|
77
|
+
from synkro.core.policy import Policy
|
|
78
|
+
|
|
79
|
+
policy = Policy.from_file("handbook.pdf") # PDF, DOCX, TXT, MD
|
|
80
|
+
pipeline = create_pipeline()
|
|
81
|
+
dataset = pipeline.generate(policy, traces=100)
|
|
82
|
+
dataset.save()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### From URLs
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from synkro.core.policy import Policy
|
|
89
|
+
|
|
90
|
+
policy = Policy.from_url("https://example.com/terms")
|
|
91
|
+
dataset = pipeline.generate(policy)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Dataset Types
|
|
95
|
+
|
|
96
|
+
| Format | Output | Best For |
|
|
97
|
+
|--------|--------|----------|
|
|
98
|
+
| **SFT** | Chat messages | Fine-tuning chat models |
|
|
99
|
+
| **QA** | Question-answer pairs | RAG systems, knowledge bases |
|
|
100
|
+
| **TOOL_CALL** | Function calling format | Training models to use custom tools |
|
|
101
|
+
|
|
102
|
+
### SFT (Default)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from synkro.types import DatasetType
|
|
106
|
+
|
|
107
|
+
pipeline = create_pipeline(dataset_type=DatasetType.SFT)
|
|
108
|
+
dataset = pipeline.generate(policy)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Output:
|
|
112
|
+
```json
|
|
113
|
+
{"messages": [
|
|
114
|
+
{"role": "system", "content": "You are a policy expert..."},
|
|
115
|
+
{"role": "user", "content": "What's the approval process for $350?"},
|
|
116
|
+
{"role": "assistant", "content": "For a $350 expense, you need..."}
|
|
117
|
+
]}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### QA
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
pipeline = create_pipeline(dataset_type=DatasetType.QA)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Output:
|
|
127
|
+
```json
|
|
128
|
+
{"question": "What's the approval process?", "answer": "You need...", "context": "..."}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Tool Calling
|
|
132
|
+
|
|
133
|
+
Generate training data for teaching models when and how to use your custom tools:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from synkro import create_pipeline, ToolDefinition, DatasetType
|
|
137
|
+
|
|
138
|
+
# Define your tools
|
|
139
|
+
web_search = ToolDefinition(
|
|
140
|
+
name="web_search",
|
|
141
|
+
description="Search the web for current information",
|
|
142
|
+
parameters={
|
|
143
|
+
"type": "object",
|
|
144
|
+
"properties": {
|
|
145
|
+
"query": {"type": "string", "description": "Search query"}
|
|
146
|
+
},
|
|
147
|
+
"required": ["query"]
|
|
148
|
+
},
|
|
149
|
+
mock_responses=["NYC: 72°F, sunny", "BTC: $67,234"]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Create pipeline with tools
|
|
153
|
+
pipeline = create_pipeline(
|
|
154
|
+
dataset_type=DatasetType.TOOL_CALL,
|
|
155
|
+
tools=[web_search],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Generate from tool usage guidelines
|
|
159
|
+
dataset = pipeline.generate("""
|
|
160
|
+
Use web_search for real-time data like weather, prices.
|
|
161
|
+
Answer general questions directly without tools.
|
|
162
|
+
""", traces=20)
|
|
163
|
+
|
|
164
|
+
dataset.save("tool_training.jsonl", format="tool_call")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Output (OpenAI function calling format):
|
|
168
|
+
```json
|
|
169
|
+
{"messages": [
|
|
170
|
+
{"role": "system", "content": "You have access to: web_search..."},
|
|
171
|
+
{"role": "user", "content": "What's the weather in NYC?"},
|
|
172
|
+
{"role": "assistant", "content": null, "tool_calls": [
|
|
173
|
+
{"id": "call_abc", "type": "function", "function": {"name": "web_search", "arguments": "{\"query\": \"weather NYC\"}"}}
|
|
174
|
+
]},
|
|
175
|
+
{"role": "tool", "tool_call_id": "call_abc", "content": "NYC: 72°F, sunny"},
|
|
176
|
+
{"role": "assistant", "content": "The weather in NYC is 72°F and sunny."}
|
|
177
|
+
]}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Evaluation & Grading
|
|
181
|
+
|
|
182
|
+
Every response is graded on policy compliance, citations, and reasoning. Failed responses are automatically refined (up to N iterations).
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from synkro.pipelines import create_pipeline
|
|
186
|
+
from synkro.models.openai import OpenAI
|
|
187
|
+
|
|
188
|
+
pipeline = create_pipeline(
|
|
189
|
+
model=OpenAI.GPT_4O_MINI, # Fast generation
|
|
190
|
+
grading_model=OpenAI.GPT_4O, # Quality grading
|
|
191
|
+
max_iterations=3, # Refinement attempts
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
dataset = pipeline.generate(policy, traces=100)
|
|
195
|
+
|
|
196
|
+
# Check quality
|
|
197
|
+
print(f"Pass rate: {dataset.passing_rate:.1%}")
|
|
198
|
+
|
|
199
|
+
# Filter to only passing traces
|
|
200
|
+
high_quality = dataset.filter(passed=True)
|
|
201
|
+
high_quality.save("training.jsonl")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## CLI
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# From file
|
|
208
|
+
synkro generate policy.pdf --traces 50 --format sft
|
|
209
|
+
|
|
210
|
+
# From text
|
|
211
|
+
synkro generate "All expenses over $50 need approval" -n 20
|
|
212
|
+
|
|
213
|
+
# From URL
|
|
214
|
+
synkro generate https://example.com/policy -o training.jsonl
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Options:**
|
|
218
|
+
- `--traces, -n` - Number of traces (default: 20)
|
|
219
|
+
- `--format, -f` - Output format: sft, qa, or tool_call (default: sft)
|
|
220
|
+
- `--output, -o` - Output file path
|
|
221
|
+
- `--model, -m` - Model for generation
|