augment-sdk 0.1.1__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.
- augment/__init__.py +30 -0
- augment/acp/__init__.py +11 -0
- augment/acp/claude_code_client.py +365 -0
- augment/acp/client.py +640 -0
- augment/acp/test_client_e2e.py +472 -0
- augment/agent.py +1139 -0
- augment/exceptions.py +92 -0
- augment/function_tools.py +265 -0
- augment/listener.py +186 -0
- augment/listener_adapter.py +83 -0
- augment/prompt_formatter.py +343 -0
- augment_sdk-0.1.1.dist-info/METADATA +841 -0
- augment_sdk-0.1.1.dist-info/RECORD +17 -0
- augment_sdk-0.1.1.dist-info/WHEEL +5 -0
- augment_sdk-0.1.1.dist-info/entry_points.txt +2 -0
- augment_sdk-0.1.1.dist-info/licenses/LICENSE +22 -0
- augment_sdk-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt formatting for agent instructions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import fields, is_dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Callable, Dict, List, Optional, Type, TypeVar, get_args, get_origin
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from .function_tools import format_function_schemas_for_prompt
|
|
11
|
+
except ImportError:
|
|
12
|
+
# When running as a script
|
|
13
|
+
from function_tools import format_function_schemas_for_prompt
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
|
|
17
|
+
# Default types for automatic type inference
|
|
18
|
+
DEFAULT_INFERENCE_TYPES = [int, float, bool, str, list, dict]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_type_name(type_hint) -> str:
|
|
22
|
+
"""Safely get the name of a type hint."""
|
|
23
|
+
if isinstance(type_hint, str):
|
|
24
|
+
return type_hint
|
|
25
|
+
if hasattr(type_hint, "__name__"):
|
|
26
|
+
return type_hint.__name__
|
|
27
|
+
return str(type_hint)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgentPromptFormatter:
|
|
31
|
+
"""
|
|
32
|
+
Formats prompts for agent instructions.
|
|
33
|
+
|
|
34
|
+
Handles:
|
|
35
|
+
- Adding function schemas to instructions
|
|
36
|
+
- Adding type requirements (typed or type inference)
|
|
37
|
+
- Creating retry prompts when parsing fails
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def prepare_instruction_prompt(
|
|
41
|
+
self,
|
|
42
|
+
instruction: str,
|
|
43
|
+
return_type: Optional[Type[T]],
|
|
44
|
+
functions: Optional[List[Callable]] = None,
|
|
45
|
+
) -> tuple[str, Dict[str, Callable]]:
|
|
46
|
+
"""
|
|
47
|
+
Prepare the instruction prompt with function schemas and type requirements.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
instruction: The base instruction from the user
|
|
51
|
+
return_type: Expected return type (None for type inference)
|
|
52
|
+
functions: Optional list of functions the agent can call
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Tuple of (prepared instruction, function_map)
|
|
56
|
+
"""
|
|
57
|
+
# Add function schemas to instruction if functions are provided
|
|
58
|
+
enhanced_instruction = instruction
|
|
59
|
+
function_map = {}
|
|
60
|
+
if functions:
|
|
61
|
+
function_schemas_text = format_function_schemas_for_prompt(functions)
|
|
62
|
+
enhanced_instruction = f"{instruction}\n\n{function_schemas_text}"
|
|
63
|
+
function_map = {func.__name__: func for func in functions}
|
|
64
|
+
|
|
65
|
+
# Determine if we're in type inference mode
|
|
66
|
+
infer_type = return_type is None
|
|
67
|
+
|
|
68
|
+
# Build the instruction with type requirements
|
|
69
|
+
if infer_type:
|
|
70
|
+
# Type inference mode - use default inference types
|
|
71
|
+
prepared_instruction = self._build_type_inference_instruction(
|
|
72
|
+
enhanced_instruction, DEFAULT_INFERENCE_TYPES
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
# Explicit type mode
|
|
76
|
+
prepared_instruction = self._build_typed_instruction(
|
|
77
|
+
enhanced_instruction, return_type
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return prepared_instruction, function_map
|
|
81
|
+
|
|
82
|
+
def prepare_retry_prompt(
|
|
83
|
+
self,
|
|
84
|
+
return_type: Optional[Type[T]],
|
|
85
|
+
error: str,
|
|
86
|
+
attempt: int,
|
|
87
|
+
) -> str:
|
|
88
|
+
"""
|
|
89
|
+
Prepare a retry prompt asking the agent to fix the output.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
return_type: Expected return type (None for type inference)
|
|
93
|
+
error: The error message from the parsing failure
|
|
94
|
+
attempt: The current attempt number
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Retry instruction prompt
|
|
98
|
+
"""
|
|
99
|
+
# Determine possible types for type inference mode
|
|
100
|
+
possible_types = DEFAULT_INFERENCE_TYPES if return_type is None else None
|
|
101
|
+
|
|
102
|
+
return self._build_retry_instruction(
|
|
103
|
+
return_type,
|
|
104
|
+
possible_types,
|
|
105
|
+
error,
|
|
106
|
+
attempt,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _build_typed_instruction(self, instruction: str, return_type: Type[T]) -> str:
|
|
110
|
+
"""
|
|
111
|
+
Build instruction with explicit type requirements.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
instruction: Base instruction
|
|
115
|
+
return_type: Expected return type
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Instruction with type requirements
|
|
119
|
+
"""
|
|
120
|
+
type_name = return_type.__name__
|
|
121
|
+
|
|
122
|
+
typed_instruction = f"""{instruction}
|
|
123
|
+
|
|
124
|
+
IMPORTANT: Provide your response in this EXACT format:
|
|
125
|
+
|
|
126
|
+
<augment-agent-message>
|
|
127
|
+
[Optional: Your explanation or reasoning]
|
|
128
|
+
</augment-agent-message>
|
|
129
|
+
|
|
130
|
+
<augment-agent-result>
|
|
131
|
+
[Your {type_name} result here]
|
|
132
|
+
</augment-agent-result>
|
|
133
|
+
|
|
134
|
+
The content inside <augment-agent-result> tags must be a valid {type_name} that can be parsed.
|
|
135
|
+
"""
|
|
136
|
+
return typed_instruction
|
|
137
|
+
|
|
138
|
+
def _build_type_inference_instruction(
|
|
139
|
+
self, instruction: str, possible_types: List[Type]
|
|
140
|
+
) -> str:
|
|
141
|
+
"""
|
|
142
|
+
Build instruction with type inference requirements.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
instruction: Base instruction
|
|
146
|
+
possible_types: List of possible types to choose from
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Instruction with type inference requirements
|
|
150
|
+
"""
|
|
151
|
+
type_names = [t.__name__ for t in possible_types]
|
|
152
|
+
types_str = ", ".join(type_names)
|
|
153
|
+
|
|
154
|
+
inference_instruction = f"""{instruction}
|
|
155
|
+
|
|
156
|
+
IMPORTANT: Provide your response in this EXACT format:
|
|
157
|
+
|
|
158
|
+
<augment-agent-message>
|
|
159
|
+
[Optional: Your explanation or reasoning]
|
|
160
|
+
</augment-agent-message>
|
|
161
|
+
|
|
162
|
+
<augment-agent-type>TYPE_NAME</augment-agent-type>
|
|
163
|
+
|
|
164
|
+
<augment-agent-result>
|
|
165
|
+
[Your result here]
|
|
166
|
+
</augment-agent-result>
|
|
167
|
+
|
|
168
|
+
Where TYPE_NAME is one of: {types_str}
|
|
169
|
+
|
|
170
|
+
Choose the most appropriate type from the list above."""
|
|
171
|
+
return inference_instruction
|
|
172
|
+
|
|
173
|
+
def _build_retry_instruction(
|
|
174
|
+
self,
|
|
175
|
+
return_type: Optional[Type[T]],
|
|
176
|
+
possible_types: Optional[List[Type]],
|
|
177
|
+
error: str,
|
|
178
|
+
attempt: int,
|
|
179
|
+
) -> str:
|
|
180
|
+
"""
|
|
181
|
+
Build retry instruction for parsing failures.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
return_type: Expected return type (None for type inference)
|
|
185
|
+
possible_types: List of possible types (for type inference mode)
|
|
186
|
+
error: Error message from parsing failure
|
|
187
|
+
attempt: Current attempt number
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Retry instruction
|
|
191
|
+
"""
|
|
192
|
+
retry_instruction = f"""The previous response could not be parsed.
|
|
193
|
+
|
|
194
|
+
Error: {error}
|
|
195
|
+
|
|
196
|
+
This is attempt {attempt}. Please provide your response again in the correct format.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
# Add detailed schema information for the expected type
|
|
200
|
+
if return_type is not None:
|
|
201
|
+
schema_info = self._get_type_schema_info(return_type)
|
|
202
|
+
if schema_info:
|
|
203
|
+
retry_instruction += f"\n{schema_info}"
|
|
204
|
+
elif possible_types:
|
|
205
|
+
type_names = [t.__name__ for t in possible_types]
|
|
206
|
+
types_str = ", ".join(type_names)
|
|
207
|
+
retry_instruction += f"\nRemember to include <augment-agent-type>TYPE_NAME</augment-agent-type> where TYPE_NAME is one of: {types_str}"
|
|
208
|
+
|
|
209
|
+
return retry_instruction
|
|
210
|
+
|
|
211
|
+
def _get_type_schema_info(self, return_type: Type) -> str:
|
|
212
|
+
"""
|
|
213
|
+
Get detailed schema information for a type to help with retry.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
return_type: The expected return type
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Schema information string, or empty string if not applicable
|
|
220
|
+
"""
|
|
221
|
+
# Generic types (list[SomeClass], dict[str, SomeClass], etc.)
|
|
222
|
+
if hasattr(return_type, "__origin__") or get_origin(return_type) is not None:
|
|
223
|
+
origin = get_origin(return_type)
|
|
224
|
+
args = get_args(return_type)
|
|
225
|
+
|
|
226
|
+
if origin is list:
|
|
227
|
+
if args and len(args) == 1:
|
|
228
|
+
element_type = args[0]
|
|
229
|
+
if is_dataclass(element_type):
|
|
230
|
+
field_info = []
|
|
231
|
+
for field in fields(element_type):
|
|
232
|
+
field_info.append(
|
|
233
|
+
f" - {field.name}: {_get_type_name(field.type)}"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return f"""
|
|
237
|
+
REMINDER: Return a JSON array of objects, each with these EXACT fields:
|
|
238
|
+
{chr(10).join(field_info)}
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
<augment-agent-result>
|
|
242
|
+
[{{"field1": value1, "field2": value2}}, {{"field1": value3, "field2": value4}}]
|
|
243
|
+
</augment-agent-result>"""
|
|
244
|
+
elif hasattr(element_type, "__name__") and issubclass(
|
|
245
|
+
element_type, Enum
|
|
246
|
+
):
|
|
247
|
+
enum_values = [f'"{e.value}"' for e in element_type]
|
|
248
|
+
return f"\nREMINDER: Return a JSON array of enum values: {enum_values}"
|
|
249
|
+
elif origin is dict:
|
|
250
|
+
return "\nREMINDER: Return a JSON object with key-value pairs."
|
|
251
|
+
|
|
252
|
+
# Dataclass
|
|
253
|
+
elif is_dataclass(return_type):
|
|
254
|
+
field_info = []
|
|
255
|
+
for field in fields(return_type):
|
|
256
|
+
field_info.append(f" - {field.name}: {_get_type_name(field.type)}")
|
|
257
|
+
|
|
258
|
+
return f"""
|
|
259
|
+
REMINDER: Return a JSON object with these EXACT fields:
|
|
260
|
+
{chr(10).join(field_info)}
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
<augment-agent-result>
|
|
264
|
+
{{"field1": value1, "field2": value2}}
|
|
265
|
+
</augment-agent-result>"""
|
|
266
|
+
|
|
267
|
+
# Enum
|
|
268
|
+
elif hasattr(return_type, "__mro__") and Enum in return_type.__mro__:
|
|
269
|
+
enum_values = [f'"{e.value}"' for e in return_type]
|
|
270
|
+
return f"\nREMINDER: Return one of these exact values: {', '.join(enum_values)}"
|
|
271
|
+
|
|
272
|
+
return ""
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def main():
|
|
276
|
+
"""Demo the prompt formatter by generating sample prompts."""
|
|
277
|
+
formatter = AgentPromptFormatter()
|
|
278
|
+
|
|
279
|
+
print("=" * 80)
|
|
280
|
+
print("PROMPT FORMATTER DEMO")
|
|
281
|
+
print("=" * 80)
|
|
282
|
+
|
|
283
|
+
# Example 1: Simple typed instruction
|
|
284
|
+
print("\n" + "=" * 80)
|
|
285
|
+
print("Example 1: Typed Instruction (return_type=int)")
|
|
286
|
+
print("=" * 80)
|
|
287
|
+
instruction = "What is 2 + 2?"
|
|
288
|
+
prompt, _ = formatter.prepare_instruction_prompt(instruction, int, None)
|
|
289
|
+
print(prompt)
|
|
290
|
+
|
|
291
|
+
# Example 2: Type inference instruction
|
|
292
|
+
print("\n" + "=" * 80)
|
|
293
|
+
print("Example 2: Type Inference Instruction (return_type=None)")
|
|
294
|
+
print("=" * 80)
|
|
295
|
+
instruction = "What is the capital of France?"
|
|
296
|
+
prompt, _ = formatter.prepare_instruction_prompt(instruction, None, None)
|
|
297
|
+
print(prompt)
|
|
298
|
+
|
|
299
|
+
# Example 3: Instruction with functions
|
|
300
|
+
print("\n" + "=" * 80)
|
|
301
|
+
print("Example 3: Instruction with Functions")
|
|
302
|
+
print("=" * 80)
|
|
303
|
+
|
|
304
|
+
def get_weather(location: str, unit: str = "celsius") -> dict:
|
|
305
|
+
"""
|
|
306
|
+
Get weather for a location.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
location: City name
|
|
310
|
+
unit: Temperature unit
|
|
311
|
+
"""
|
|
312
|
+
return {"temp": 72, "condition": "sunny"}
|
|
313
|
+
|
|
314
|
+
instruction = "What's the weather in San Francisco?"
|
|
315
|
+
prompt, func_map = formatter.prepare_instruction_prompt(
|
|
316
|
+
instruction, dict, [get_weather]
|
|
317
|
+
)
|
|
318
|
+
print(prompt)
|
|
319
|
+
print(f"\nFunction map: {list(func_map.keys())}")
|
|
320
|
+
|
|
321
|
+
# Example 4: Retry prompt
|
|
322
|
+
print("\n" + "=" * 80)
|
|
323
|
+
print("Example 4: Retry Prompt (after parsing failure)")
|
|
324
|
+
print("=" * 80)
|
|
325
|
+
error = "Could not parse 'forty-two' as int"
|
|
326
|
+
retry_prompt = formatter.prepare_retry_prompt(int, error, 1)
|
|
327
|
+
print(retry_prompt)
|
|
328
|
+
|
|
329
|
+
# Example 5: Type inference retry prompt
|
|
330
|
+
print("\n" + "=" * 80)
|
|
331
|
+
print("Example 5: Type Inference Retry Prompt")
|
|
332
|
+
print("=" * 80)
|
|
333
|
+
error = "Missing <response_type> tag"
|
|
334
|
+
retry_prompt = formatter.prepare_retry_prompt(None, error, 2)
|
|
335
|
+
print(retry_prompt)
|
|
336
|
+
|
|
337
|
+
print("\n" + "=" * 80)
|
|
338
|
+
print("END OF DEMO")
|
|
339
|
+
print("=" * 80)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
if __name__ == "__main__":
|
|
343
|
+
main()
|