-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathartifact_codec.go
More file actions
157 lines (142 loc) · 4.58 KB
/
Copy pathartifact_codec.go
File metadata and controls
157 lines (142 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package contexty
import (
"encoding/json"
"errors"
"fmt"
)
// ArtifactCodecDescriptor describes a host-owned artifact schema without binding
// contexty to the host domain.
type ArtifactCodecDescriptor[T any] struct {
TypeID string
Kind ArtifactKind
Render func(T) string
Lifecycle ArtifactLifecycle
BoundTurnID string
OwnerRef *SourceRef
SourceRefs []SourceRef
MergePolicy MergePolicy
Budget *ArtifactBudgetPolicy
Persistence ArtifactPersistencePolicy
}
// TypedArtifactCodec decodes one artifact schema.
type TypedArtifactCodec interface {
TypeID() string
DecodeArtifact(artifact ContextArtifact) (any, error)
}
type typedArtifactCodec[T any] struct {
desc ArtifactCodecDescriptor[T]
}
// NewTypedArtifactCodec creates a registry entry for a typed artifact schema.
func NewTypedArtifactCodec[T any](desc ArtifactCodecDescriptor[T]) TypedArtifactCodec {
return typedArtifactCodec[T]{desc: desc}
}
func (c typedArtifactCodec[T]) TypeID() string { return c.desc.TypeID }
func (c typedArtifactCodec[T]) DecodeArtifact(artifact ContextArtifact) (any, error) {
return DecodeTypedArtifact[T](artifact, c.desc)
}
// ArtifactCodecRegistry stores host-provided artifact codecs.
type ArtifactCodecRegistry struct {
codecs map[string]TypedArtifactCodec
}
// NewArtifactCodecRegistry returns an empty artifact codec registry.
func NewArtifactCodecRegistry() *ArtifactCodecRegistry {
return &ArtifactCodecRegistry{codecs: make(map[string]TypedArtifactCodec)}
}
// Register adds a typed artifact codec. It panics on duplicate type IDs.
func (r *ArtifactCodecRegistry) Register(codec TypedArtifactCodec) {
if r == nil || codec == nil {
return
}
typeID := codec.TypeID()
if typeID == "" {
panic("contexty: artifact codec type_id is empty")
}
if r.codecs == nil {
r.codecs = make(map[string]TypedArtifactCodec)
}
if _, exists := r.codecs[typeID]; exists {
panic(fmt.Sprintf("contexty: duplicate artifact codec %q", typeID))
}
r.codecs[typeID] = codec
}
// Decode decodes an artifact through the registered codec matching ArtifactType.
func (r *ArtifactCodecRegistry) Decode(artifact ContextArtifact) (any, error) {
if r == nil {
return nil, errors.New("contexty: artifact decode: registry is nil")
}
codec, ok := r.codecs[artifact.ArtifactType]
if !ok {
return nil, fmt.Errorf("contexty: artifact decode: unregistered type_id %q", artifact.ArtifactType)
}
return codec.DecodeArtifact(artifact)
}
// NewTypedArtifact creates a ContextArtifact from a host-owned structured value.
func NewTypedArtifact[T any](id string, desc ArtifactCodecDescriptor[T], value T) (ContextArtifact, error) {
if desc.TypeID == "" {
return ContextArtifact{}, errors.New("contexty: typed artifact: type_id is empty")
}
data, err := json.Marshal(value)
if err != nil {
return ContextArtifact{}, fmt.Errorf("contexty: typed artifact %q: %w", desc.TypeID, err)
}
payload := ToolPayload{
Text: "",
Data: data,
Binary: nil,
MIMEType: "application/json",
Error: nil,
Progress: nil,
Control: nil,
}
if desc.Render != nil {
payload.Text = desc.Render(value)
}
return ContextArtifact{
ID: id,
Kind: desc.Kind,
ArtifactType: desc.TypeID,
Payload: payload,
Lifecycle: artifactDescriptorLifecycle(desc.Lifecycle),
BoundTurnID: desc.BoundTurnID,
OwnerRef: cloneSourceRefPtr(desc.OwnerRef),
SourceRefs: cloneSourceRefs(desc.SourceRefs),
MergePolicy: desc.MergePolicy,
Budget: cloneArtifactBudget(desc.Budget),
Persistence: desc.Persistence,
}, nil
}
func artifactDescriptorLifecycle(lifecycle ArtifactLifecycle) ArtifactLifecycle {
if lifecycle == "" {
return ArtifactLifecycleTurnBound
}
return lifecycle
}
func cloneArtifactBudget(in *ArtifactBudgetPolicy) *ArtifactBudgetPolicy {
if in == nil {
return nil
}
cp := *in
return &cp
}
// DecodeTypedArtifact decodes a structured value from a typed artifact.
func DecodeTypedArtifact[T any](artifact ContextArtifact, desc ArtifactCodecDescriptor[T]) (T, error) {
var zero T
if desc.TypeID == "" {
return zero, errors.New("contexty: typed artifact decode: type_id is empty")
}
if artifact.ArtifactType != desc.TypeID {
return zero, fmt.Errorf(
"contexty: typed artifact decode: expected %q, got %q",
desc.TypeID,
artifact.ArtifactType,
)
}
if len(artifact.Payload.Data) == 0 {
return zero, fmt.Errorf("contexty: typed artifact decode %q: payload data is empty", desc.TypeID)
}
var out T
if err := json.Unmarshal(artifact.Payload.Data, &out); err != nil {
return zero, fmt.Errorf("contexty: typed artifact decode %q: %w", desc.TypeID, err)
}
return out, nil
}