Skip to content

Commit e7be33f

Browse files
committed
Add PromptBuilder markdown features and functional Tool creation
1 parent cc63319 commit e7be33f

5 files changed

Lines changed: 889 additions & 46 deletions

File tree

README.md

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,31 +81,27 @@ type OrderSchema struct {
8181
Address string `json:"address" description:"Delivery address" required:"true"`
8282
}
8383

84-
// Implement the Tool interface
85-
type OrderTool struct{}
86-
87-
func (t *OrderTool) GetDefinition() syndicate.ToolDefinition {
88-
schema, _ := syndicate.GenerateRawSchema(OrderSchema{})
89-
return syndicate.ToolDefinition{
90-
Name: "ProcessOrder",
91-
Description: "Process customer orders",
92-
Parameters: schema,
93-
}
94-
}
95-
96-
func (t *OrderTool) Execute(args json.RawMessage) (interface{}, error) {
97-
var order OrderSchema
98-
json.Unmarshal(args, &order)
99-
// Process the order...
100-
return "Order processed successfully", nil
101-
}
84+
// Create a tool with functional options
85+
tool, _ := syndicate.NewTool(
86+
syndicate.WithToolName("ProcessOrder"),
87+
syndicate.WithToolDescription("Process customer orders"),
88+
syndicate.WithToolSchema(OrderSchema{}),
89+
syndicate.WithToolExecuteHandler(func(args json.RawMessage) (interface{}, error) {
90+
var order OrderSchema
91+
if err := json.Unmarshal(args, &order); err != nil {
92+
return nil, err
93+
}
94+
// Process the order...
95+
return "Order processed successfully", nil
96+
}),
97+
)
10298

10399
// Create agent with tool
104100
agent, _ := syndicate.NewAgent(
105101
syndicate.WithClient(client),
106102
syndicate.WithName("OrderAgent"),
107103
syndicate.WithSystemPrompt("You process customer orders."),
108-
syndicate.WithTools(&OrderTool{}),
104+
syndicate.WithTools(tool),
109105
syndicate.WithMemory(syndicate.NewSimpleMemory()),
110106
)
111107
```
@@ -183,7 +179,21 @@ agent, _ := syndicate.NewAgent(
183179
<details>
184180
<summary><b>Tool Integration</b></summary>
185181

186-
Tools allow agents to interact with external systems. Implement the `Tool` interface:
182+
Tools allow agents to interact with external systems. You can create tools easily using the functional options pattern:
183+
184+
```go
185+
tool, err := syndicate.NewTool(
186+
syndicate.WithToolName("ToolName"),
187+
syndicate.WithToolDescription("Tool description"),
188+
syndicate.WithToolSchema(YourSchema{}),
189+
syndicate.WithToolExecuteHandler(func(args json.RawMessage) (interface{}, error) {
190+
// Your implementation here
191+
return result, nil
192+
}),
193+
)
194+
```
195+
196+
Alternatively, you can implement the `Tool` interface directly:
187197

188198
```go
189199
type Tool interface {
@@ -216,18 +226,76 @@ type Memory interface {
216226
<details>
217227
<summary><b>Prompt Building</b></summary>
218228

219-
Create structured prompts with the builder:
229+
Create structured prompts with the builder, now with comprehensive markdown support:
220230

221231
```go
222232
prompt := syndicate.NewPromptBuilder().
233+
// Basic sections and text
223234
CreateSection("Role").
224235
AddText("Role", "You are a customer service agent.").
225-
CreateSection("Guidelines").
226-
AddListItem("Guidelines", "Be helpful and professional.").
227-
AddListItem("Guidelines", "Always ask for clarification.").
236+
237+
// Formatting options
238+
CreateSection("Instructions").
239+
AddHeader("Instructions", "Important Guidelines", 2).
240+
AddBoldText("Instructions", "Follow these rules carefully:").
241+
AddBulletItem("Instructions", "Be helpful and professional").
242+
AddBulletItem("Instructions", "Use clear, concise language").
243+
AddListItem("Instructions", "Verify customer information first").
244+
AddListItem("Instructions", "Solve the customer's problem").
245+
AddBlockquote("Instructions", "Customer satisfaction is our priority").
246+
247+
// Code examples
248+
CreateSection("Examples").
249+
AddText("Examples", "Here's how to greet a customer:").
250+
AddCodeBlock("Examples", `function greet(name) {
251+
return "Hello " + name + ", how can I help you today?";
252+
}`, "javascript").
253+
254+
// Tables and links
255+
CreateSection("Resources").
256+
AddLink("Resources", "Customer Knowledge Base", "https://example.com/kb").
257+
AddHorizontalRule("Resources").
258+
AddTable("Resources",
259+
[]string{"Resource Type", "URL", "Description"},
260+
[][]string{
261+
{"FAQ", "https://example.com/faq", "Frequently asked questions"},
262+
{"Policy", "https://example.com/policy", "Company policies"},
263+
}).
264+
228265
Build()
229266
```
230267

268+
The PromptBuilder combines XML-style hierarchical structure with markdown formatting for optimal LLM prompting.
269+
270+
Basic table example:
271+
272+
```go
273+
// Basic table example
274+
pb := syndicate.NewPromptBuilder().
275+
CreateSection("Tables").
276+
AddText("Tables", "Here's a simple table:").
277+
AddTable("Tables",
278+
[]string{"Name", "Age", "Role"}, // Headers
279+
[][]string{ // Rows
280+
{"John", "30", "Developer"},
281+
{"Jane", "28", "Designer"},
282+
{"Bob", "35", "Manager"},
283+
})
284+
```
285+
286+
This produces a markdown table like:
287+
288+
```
289+
<Tables>
290+
Here's a simple table:
291+
| Name | Age | Role |
292+
| --- | --- | --- |
293+
| John | 30 | Developer |
294+
| Jane | 28 | Designer |
295+
| Bob | 35 | Manager |
296+
</Tables>
297+
```
298+
231299
</details>
232300

233301
## 🔧 Configuration

prompt.go

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,96 @@ func (s *Section) addListItem(item string) {
2828
s.addLine(line) // Append the formatted list item as a new line.
2929
}
3030

31+
// addBulletItem formats and adds a bullet point item to the Section.
32+
func (s *Section) addBulletItem(item string) {
33+
line := fmt.Sprintf("- %s", item) // Format the item with a bullet point.
34+
s.addLine(line) // Append the formatted bullet item as a new line.
35+
}
36+
37+
// addCodeBlock formats and adds a code block with optional language specification.
38+
func (s *Section) addCodeBlock(code, language string) {
39+
if language != "" {
40+
s.addLine(fmt.Sprintf("```%s", language))
41+
} else {
42+
s.addLine("```")
43+
}
44+
s.addLine(code)
45+
s.addLine("```")
46+
}
47+
48+
// addBoldText formats and adds bold text.
49+
func (s *Section) addBoldText(text string) {
50+
s.addLine(fmt.Sprintf("**%s**", text))
51+
}
52+
53+
// addItalicText formats and adds italic text.
54+
func (s *Section) addItalicText(text string) {
55+
s.addLine(fmt.Sprintf("*%s*", text))
56+
}
57+
58+
// addHeader formats and adds a header of specified level (1-6).
59+
func (s *Section) addHeader(text string, level int) {
60+
if level < 1 {
61+
level = 1
62+
} else if level > 6 {
63+
level = 6
64+
}
65+
prefix := strings.Repeat("#", level)
66+
s.addLine(fmt.Sprintf("%s %s", prefix, text))
67+
}
68+
69+
// addBlockquote formats and adds a blockquote.
70+
func (s *Section) addBlockquote(text string) {
71+
// Split multi-line quotes and prefix each line
72+
lines := strings.Split(text, "\n")
73+
for _, line := range lines {
74+
s.addLine(fmt.Sprintf("> %s", strings.TrimSpace(line)))
75+
}
76+
}
77+
78+
// addLink formats and adds a hyperlink.
79+
func (s *Section) addLink(text, url string) {
80+
s.addLine(fmt.Sprintf("[%s](%s)", text, url))
81+
}
82+
83+
// addHorizontalRule adds a horizontal rule.
84+
func (s *Section) addHorizontalRule() {
85+
s.addLine("---")
86+
}
87+
88+
// addTable formats and adds a simple table with headers and rows.
89+
func (s *Section) addTable(headers []string, rows [][]string) {
90+
if len(headers) == 0 {
91+
return
92+
}
93+
94+
// Add header row
95+
headerLine := "| " + strings.Join(headers, " | ") + " |"
96+
s.addLine(headerLine)
97+
98+
// Add separator row
99+
separators := make([]string, len(headers))
100+
for i := range separators {
101+
separators[i] = "---"
102+
}
103+
separatorLine := "| " + strings.Join(separators, " | ") + " |"
104+
s.addLine(separatorLine)
105+
106+
// Add data rows
107+
for _, row := range rows {
108+
if len(row) > len(headers) {
109+
row = row[:len(headers)]
110+
} else if len(row) < len(headers) {
111+
// Pad with empty cells
112+
for i := len(row); i < len(headers); i++ {
113+
row = append(row, "")
114+
}
115+
}
116+
rowLine := "| " + strings.Join(row, " | ") + " |"
117+
s.addLine(rowLine)
118+
}
119+
}
120+
31121
// findSubSection recursively searches for a subsection by its name within the current section.
32122
// If a matching subsection is found, it is returned; otherwise, nil is returned.
33123
func (s *Section) findSubSection(name string) *Section {
@@ -161,6 +251,116 @@ func (pb *PromptBuilder) AddListItemF(sectionName string, value interface{}) *Pr
161251
return pb.AddListItem(sectionName, text)
162252
}
163253

254+
// AddBulletItem adds a bullet point item to the specified section or subsection.
255+
func (pb *PromptBuilder) AddBulletItem(sectionName, item string) *PromptBuilder {
256+
if section := pb.findSection(sectionName); section != nil {
257+
section.addBulletItem(strings.TrimSpace(item))
258+
}
259+
return pb
260+
}
261+
262+
// AddBulletItemF is a helper method that converts any value to its string representation
263+
// and adds it as a bullet point item to the specified section.
264+
func (pb *PromptBuilder) AddBulletItemF(sectionName string, value interface{}) *PromptBuilder {
265+
var text string
266+
267+
if str, ok := value.(string); ok {
268+
text = str
269+
} else {
270+
bytes, err := json.Marshal(value)
271+
if err != nil {
272+
text = fmt.Sprintf("%v", value)
273+
} else {
274+
text = string(bytes)
275+
}
276+
}
277+
278+
return pb.AddBulletItem(sectionName, text)
279+
}
280+
281+
// AddCodeBlock adds a code block with optional language specification to the specified section.
282+
func (pb *PromptBuilder) AddCodeBlock(sectionName, code, language string) *PromptBuilder {
283+
if section := pb.findSection(sectionName); section != nil {
284+
section.addCodeBlock(code, language)
285+
}
286+
return pb
287+
}
288+
289+
// AddCodeBlockF is a helper method that converts any value to its string representation
290+
// and adds it as a code block to the specified section.
291+
func (pb *PromptBuilder) AddCodeBlockF(sectionName string, value interface{}, language string) *PromptBuilder {
292+
var text string
293+
294+
if str, ok := value.(string); ok {
295+
text = str
296+
} else {
297+
bytes, err := json.Marshal(value)
298+
if err != nil {
299+
text = fmt.Sprintf("%v", value)
300+
} else {
301+
text = string(bytes)
302+
}
303+
}
304+
305+
return pb.AddCodeBlock(sectionName, text, language)
306+
}
307+
308+
// AddBoldText adds bold text to the specified section.
309+
func (pb *PromptBuilder) AddBoldText(sectionName, text string) *PromptBuilder {
310+
if section := pb.findSection(sectionName); section != nil {
311+
section.addBoldText(strings.TrimSpace(text))
312+
}
313+
return pb
314+
}
315+
316+
// AddItalicText adds italic text to the specified section.
317+
func (pb *PromptBuilder) AddItalicText(sectionName, text string) *PromptBuilder {
318+
if section := pb.findSection(sectionName); section != nil {
319+
section.addItalicText(strings.TrimSpace(text))
320+
}
321+
return pb
322+
}
323+
324+
// AddHeader adds a header of specified level to the section.
325+
func (pb *PromptBuilder) AddHeader(sectionName, text string, level int) *PromptBuilder {
326+
if section := pb.findSection(sectionName); section != nil {
327+
section.addHeader(strings.TrimSpace(text), level)
328+
}
329+
return pb
330+
}
331+
332+
// AddBlockquote adds a blockquote to the specified section.
333+
func (pb *PromptBuilder) AddBlockquote(sectionName, text string) *PromptBuilder {
334+
if section := pb.findSection(sectionName); section != nil {
335+
section.addBlockquote(strings.TrimSpace(text))
336+
}
337+
return pb
338+
}
339+
340+
// AddLink adds a hyperlink to the specified section.
341+
func (pb *PromptBuilder) AddLink(sectionName, text, url string) *PromptBuilder {
342+
if section := pb.findSection(sectionName); section != nil {
343+
section.addLink(strings.TrimSpace(text), url)
344+
}
345+
return pb
346+
}
347+
348+
// AddHorizontalRule adds a horizontal rule to the specified section.
349+
func (pb *PromptBuilder) AddHorizontalRule(sectionName string) *PromptBuilder {
350+
if section := pb.findSection(sectionName); section != nil {
351+
section.addHorizontalRule()
352+
}
353+
return pb
354+
}
355+
356+
// AddTable adds a table with headers and rows to the specified section.
357+
func (pb *PromptBuilder) AddTable(sectionName string, headers []string, rows [][]string) *PromptBuilder {
358+
if section := pb.findSection(sectionName); section != nil {
359+
section.addTable(headers, rows)
360+
}
361+
return pb
362+
}
363+
164364
// buildSection recursively generates the string representation of a Section and its nested subsections.
165365
// The indent parameter is used to properly format nested content.
166366
func buildSection(sec *Section, indent string) string {
@@ -172,7 +372,7 @@ func buildSection(sec *Section, indent string) string {
172372
// Append each line of the section, ensuring proper trimming and formatting.
173373
for _, line := range sec.Lines {
174374
sb.WriteString(indent)
175-
sb.WriteString(strings.TrimSpace(line))
375+
sb.WriteString(line)
176376
sb.WriteString("\n")
177377
}
178378

0 commit comments

Comments
 (0)