• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kshard / chatter / 16538940978

26 Jul 2025 10:35AM UTC coverage: 67.584% (+44.9%) from 22.674%
16538940978

Pull #53

github

fogfish
update license
Pull Request #53: Enable multi-content I/O within prompts & responses

596 of 837 new or added lines in 27 files covered. (71.21%)

2 existing lines in 2 files now uncovered.

884 of 1308 relevant lines covered (67.58%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.96
/provider/bedrock/foundation/converse/encoder.go
1
//
2
// Copyright (C) 2024 - 2025 Dmitry Kolesnikov
3
//
4
// This file may be modified and distributed under the terms
5
// of the MIT license.  See the LICENSE file for details.
6
// https://github.com/kshard/chatter
7
//
8

9
package converse
10

11
import (
12
        "encoding/json"
13
        "log/slog"
14

15
        "github.com/aws/aws-sdk-go-v2/aws"
16
        "github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
17
        "github.com/aws/aws-sdk-go-v2/service/bedrockruntime/document"
18
        "github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types"
19
        "github.com/kshard/chatter"
20
        "github.com/kshard/chatter/aio/provider"
21
)
22

23
func factory(model string, registry chatter.Registry) func() (provider.Encoder[*bedrockruntime.ConverseInput], error) {
1✔
24
        return func() (provider.Encoder[*bedrockruntime.ConverseInput], error) {
2✔
25
                var toolConfig *types.ToolConfiguration
1✔
26
                if len(registry) > 0 {
2✔
27
                        reg, err := encodeRegistry(registry)
1✔
28
                        if err != nil {
1✔
NEW
29
                                return nil, err
×
NEW
30
                        }
×
31
                        toolConfig = reg
1✔
32
                }
33

34
                return &encoder{
1✔
35
                        req: &bedrockruntime.ConverseInput{
1✔
36
                                InferenceConfig: &types.InferenceConfiguration{},
1✔
37
                                ModelId:         aws.String(model),
1✔
38
                                Messages:        make([]types.Message, 0),
1✔
39
                                System:          make([]types.SystemContentBlock, 0),
1✔
40
                                ToolConfig:      toolConfig,
1✔
41
                        },
1✔
42
                }, nil
1✔
43
        }
44
}
45

46
func (codec *encoder) WithInferrer(inferrer provider.Inferrer) {
1✔
47
        codec.req.InferenceConfig.Temperature = aws.Float32(float32(inferrer.Temperature))
1✔
48
        codec.req.InferenceConfig.TopP = aws.Float32(float32(inferrer.TopP))
1✔
49
        codec.req.InferenceConfig.MaxTokens = aws.Int32(int32(inferrer.MaxTokens))
1✔
50
        codec.req.InferenceConfig.StopSequences = inferrer.StopSequences
1✔
51
}
1✔
52

53
func (codec *encoder) WithCommand(cmd chatter.Cmd) {
1✔
54
        tool, err := encodeCommand(cmd)
1✔
55
        if err != nil {
2✔
56
                slog.Warn("invalid tool configuration", "cmd", cmd.Cmd, "err", err)
1✔
57
                return
1✔
58
        }
1✔
59
        if codec.req.ToolConfig == nil {
2✔
60
                codec.req.ToolConfig = &types.ToolConfiguration{
1✔
61
                        ToolChoice: &types.ToolChoiceMemberAuto{},
1✔
62
                        Tools:      []types.Tool{},
1✔
63
                }
1✔
64
        }
1✔
65

66
        codec.req.ToolConfig.Tools = append(codec.req.ToolConfig.Tools, tool)
1✔
67
}
68

69
// AsStratum processes a Stratum message (system role)
70
func (codec *encoder) AsStratum(stratum chatter.Stratum) error {
1✔
71
        codec.req.System = append(codec.req.System,
1✔
72
                &types.SystemContentBlockMemberText{Value: string(stratum)},
1✔
73
        )
1✔
74
        return nil
1✔
75
}
1✔
76

77
// AsText processes a Text message as user input
78
func (codec *encoder) AsText(text chatter.Text) error {
1✔
79
        msg := types.Message{
1✔
80
                Role: types.ConversationRoleUser,
1✔
81
                Content: []types.ContentBlock{
1✔
82
                        &types.ContentBlockMemberText{Value: string(text)},
1✔
83
                },
1✔
84
        }
1✔
85

1✔
86
        codec.req.Messages = append(codec.req.Messages, msg)
1✔
87
        return nil
1✔
88
}
1✔
89

90
// AsPrompt processes a Prompt message by converting it to string
91
func (codec *encoder) AsPrompt(prompt *chatter.Prompt) error {
1✔
92
        msg := types.Message{
1✔
93
                Role: types.ConversationRoleUser,
1✔
94
                Content: []types.ContentBlock{
1✔
95
                        &types.ContentBlockMemberText{Value: prompt.String()},
1✔
96
                },
1✔
97
        }
1✔
98

1✔
99
        codec.req.Messages = append(codec.req.Messages, msg)
1✔
100
        return nil
1✔
101
}
1✔
102

103
// AsAnswer processes an Answer message (tool results)
104
func (codec *encoder) AsAnswer(answer *chatter.Answer) error {
1✔
105
        if len(answer.Yield) == 0 {
2✔
106
                return nil
1✔
107
        }
1✔
108

109
        msg := types.Message{
1✔
110
                Role:    types.ConversationRoleUser,
1✔
111
                Content: []types.ContentBlock{},
1✔
112
        }
1✔
113
        for _, yield := range answer.Yield {
2✔
114
                var reply any
1✔
115
                if err := json.Unmarshal(yield.Value, &reply); err != nil {
1✔
NEW
116
                        return err
×
NEW
117
                }
×
118
                msg.Content = append(msg.Content,
1✔
119
                        &types.ContentBlockMemberToolResult{
1✔
120
                                Value: types.ToolResultBlock{
1✔
121
                                        ToolUseId: aws.String(yield.ID),
1✔
122
                                        Content: []types.ToolResultContentBlock{
1✔
123
                                                &types.ToolResultContentBlockMemberJson{
1✔
124
                                                        Value: document.NewLazyDocument(
1✔
125
                                                                map[string]any{"json": reply},
1✔
126
                                                        ),
1✔
127
                                                },
1✔
128
                                        },
1✔
129
                                },
1✔
130
                        },
1✔
131
                )
1✔
132
        }
133

134
        codec.req.Messages = append(codec.req.Messages, msg)
1✔
135
        return nil
1✔
136
}
137

138
// AsReply processes a Reply message (assistant response)
139
func (codec *encoder) AsReply(reply *chatter.Reply) error {
1✔
140
        msg := types.Message{
1✔
141
                Role:    types.ConversationRoleAssistant,
1✔
142
                Content: []types.ContentBlock{},
1✔
143
        }
1✔
144

1✔
145
        for _, block := range reply.Content {
2✔
146
                switch v := (block).(type) {
1✔
147
                case chatter.Text:
1✔
148
                        msg.Content = append(msg.Content,
1✔
149
                                &types.ContentBlockMemberText{Value: string(v)},
1✔
150
                        )
1✔
NEW
151
                case interface{ RawMessage() any }:
×
NEW
152
                        if cb, ok := v.RawMessage().(types.ContentBlock); ok {
×
NEW
153
                                msg.Content = append(msg.Content, cb)
×
NEW
154
                        }
×
155
                }
156
        }
157

158
        codec.req.Messages = append(codec.req.Messages, msg)
1✔
159
        return nil
1✔
160
}
161

162
func (codec *encoder) Build() *bedrockruntime.ConverseInput {
1✔
163
        return codec.req
1✔
164
}
1✔
165

166
func encodeRegistry(registry chatter.Registry) (*types.ToolConfiguration, error) {
1✔
167
        tools := &types.ToolConfiguration{
1✔
168
                ToolChoice: &types.ToolChoiceMemberAuto{},
1✔
169
                Tools:      []types.Tool{},
1✔
170
        }
1✔
171

1✔
172
        for _, reg := range registry {
2✔
173
                cmd, err := encodeCommand(reg)
1✔
174
                if err != nil {
1✔
NEW
175
                        return nil, err
×
NEW
176
                }
×
177
                tools.Tools = append(tools.Tools, cmd)
1✔
178
        }
179

180
        return tools, nil
1✔
181
}
182

183
func encodeCommand(cmd chatter.Cmd) (types.Tool, error) {
1✔
184
        var spec any
1✔
185
        if err := json.Unmarshal(cmd.Schema, &spec); err != nil {
2✔
186
                return nil, err
1✔
187
        }
1✔
188

189
        return &types.ToolMemberToolSpec{
1✔
190
                Value: types.ToolSpecification{
1✔
191
                        Name:        aws.String(cmd.Cmd),
1✔
192
                        Description: aws.String(cmd.About),
1✔
193
                        InputSchema: &types.ToolInputSchemaMemberJson{
1✔
194
                                Value: document.NewLazyDocument(spec),
1✔
195
                        },
1✔
196
                },
1✔
197
        }, nil
1✔
198
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc