Skip to content

Commit 4a2da82

Browse files
committed
Dump proposal API
1 parent 5c46696 commit 4a2da82

8 files changed

Lines changed: 4195 additions & 0 deletions

File tree

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
/*
2+
Copyright 2026.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
corev1 "k8s.io/api/core/v1"
21+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
)
24+
25+
// OutputFieldType is the JSON type of an output field in the agent's
26+
// structured output schema. The operator merges these fields into the
27+
// base output schema that every agent produces (diagnosis, proposal,
28+
// verification plan, RBAC request), allowing adapters to request
29+
// domain-specific structured data from the agent.
30+
// +kubebuilder:validation:Enum=string;number;boolean;array;object
31+
type OutputFieldType string
32+
33+
const (
34+
OutputFieldTypeString OutputFieldType = "string"
35+
OutputFieldTypeNumber OutputFieldType = "number"
36+
OutputFieldTypeBoolean OutputFieldType = "boolean"
37+
OutputFieldTypeArray OutputFieldType = "array"
38+
OutputFieldTypeObject OutputFieldType = "object"
39+
)
40+
41+
// OutputField defines a top-level field in the agent's structured output.
42+
// These fields are merged into the base output schema that the operator sends
43+
// to the agent. Use outputFields to request adapter-specific structured data
44+
// (e.g., an ACS adapter might add a "violationId" string field).
45+
//
46+
// Supports up to two levels of nesting: top-level fields can contain object
47+
// properties or array items, and those can contain one more level of nesting.
48+
//
49+
// Example — adding an ACS violation ID and affected images to the output:
50+
//
51+
// outputFields:
52+
// - name: violationId
53+
// type: string
54+
// description: "The ACS violation ID that triggered this proposal"
55+
// required: true
56+
// - name: affectedImages
57+
// type: array
58+
// description: "Container images flagged by the violation"
59+
// items:
60+
// type: string
61+
//
62+
// +kubebuilder:validation:XValidation:rule="self.type == 'array' ? has(self.items) : true",message="items is required when type is array"
63+
// +kubebuilder:validation:XValidation:rule="self.type == 'object' ? has(self.properties) : true",message="properties is required when type is object"
64+
// +kubebuilder:validation:XValidation:rule="has(self.enum) ? self.type == 'string' : true",message="enum is only valid for string fields"
65+
type OutputField struct {
66+
// name is the field name in the output JSON.
67+
// +kubebuilder:validation:Required
68+
// +kubebuilder:validation:MinLength=1
69+
// +kubebuilder:validation:Pattern="^[a-zA-Z][a-zA-Z0-9_]*$"
70+
Name string `json:"name"`
71+
72+
// type is the JSON type of this field.
73+
// +kubebuilder:validation:Required
74+
Type OutputFieldType `json:"type"`
75+
76+
// description explains the purpose of this field (passed to the LLM).
77+
// +optional
78+
Description string `json:"description,omitempty"`
79+
80+
// required indicates whether the agent must populate this field.
81+
// +optional
82+
Required bool `json:"required,omitempty"`
83+
84+
// enum constrains string fields to a set of allowed values.
85+
// +optional
86+
Enum []string `json:"enum,omitempty"`
87+
88+
// items defines the element schema when type is array.
89+
// +optional
90+
Items *OutputFieldItems `json:"items,omitempty"`
91+
92+
// properties defines nested fields when type is object.
93+
// +optional
94+
// +listType=map
95+
// +listMapKey=name
96+
Properties []OutputSubField `json:"properties,omitempty"`
97+
}
98+
99+
// OutputSubField defines a nested field (one level deep) within an OutputField
100+
// of type "object" or within array items of type "object". At this depth,
101+
// array items are restricted to primitive types (string, number, boolean).
102+
// +kubebuilder:validation:XValidation:rule="self.type == 'array' ? has(self.items) : true",message="items is required when type is array"
103+
// +kubebuilder:validation:XValidation:rule="has(self.enum) ? self.type == 'string' : true",message="enum is only valid for string fields"
104+
type OutputSubField struct {
105+
// name is the field name in the output JSON.
106+
// +kubebuilder:validation:Required
107+
// +kubebuilder:validation:MinLength=1
108+
// +kubebuilder:validation:Pattern="^[a-zA-Z][a-zA-Z0-9_]*$"
109+
Name string `json:"name"`
110+
111+
// type is the JSON type of this field.
112+
// +kubebuilder:validation:Required
113+
Type OutputFieldType `json:"type"`
114+
115+
// description explains the purpose of this field (passed to the LLM).
116+
// +optional
117+
Description string `json:"description,omitempty"`
118+
119+
// required indicates whether the agent must populate this field.
120+
// +optional
121+
Required bool `json:"required,omitempty"`
122+
123+
// enum constrains string fields to a set of allowed values.
124+
// +optional
125+
Enum []string `json:"enum,omitempty"`
126+
127+
// items defines the element schema when type is array (primitive types only at this depth).
128+
// +optional
129+
Items *OutputSubFieldItems `json:"items,omitempty"`
130+
}
131+
132+
// OutputFieldItems defines the schema for array elements at the top level.
133+
// Supports primitive types and objects (with nested OutputSubField properties).
134+
type OutputFieldItems struct {
135+
// type is the JSON type of array elements.
136+
// +kubebuilder:validation:Required
137+
// +kubebuilder:validation:Enum=string;number;boolean;object
138+
Type OutputFieldType `json:"type"`
139+
140+
// properties defines fields for object-typed array elements.
141+
// +optional
142+
// +listType=map
143+
// +listMapKey=name
144+
Properties []OutputSubField `json:"properties,omitempty"`
145+
}
146+
147+
// OutputSubFieldItems defines the schema for array elements at the nested
148+
// level. Only primitive types (string, number, boolean) are allowed here
149+
// to prevent unbounded schema depth.
150+
type OutputSubFieldItems struct {
151+
// type is the JSON type of array elements.
152+
// +kubebuilder:validation:Required
153+
// +kubebuilder:validation:Enum=string;number;boolean
154+
Type OutputFieldType `json:"type"`
155+
}
156+
157+
// NOTE: MCPHeaderSourceType, MCPHeaderValueSource, MCPHeader, and MCPServerConfig
158+
// are defined in olsconfig_types.go and shared by both OLSConfig and Agent.
159+
160+
// SkillsSource defines an OCI image containing skills and optionally which
161+
// paths within that image to mount. Skills are mounted as Kubernetes image
162+
// volumes in the agent's sandbox pod.
163+
//
164+
// When paths is omitted, the entire image is mounted. When paths is specified,
165+
// only those directories are mounted (each as a separate subPath volumeMount),
166+
// allowing selective composition of skills from large shared images.
167+
//
168+
// Example — mount all skills from a custom image:
169+
//
170+
// skills:
171+
// - image: quay.io/my-org/my-skills:latest
172+
//
173+
// Example — selectively mount two skills from a shared image:
174+
//
175+
// skills:
176+
// - image: registry.ci.openshift.org/ocp/5.0:agentic-skills
177+
// paths:
178+
// - /skills/prometheus
179+
// - /skills/cluster-update/update-advisor
180+
type SkillsSource struct {
181+
// image is the OCI image reference containing skills.
182+
// The operator mounts this as a Kubernetes image volume (requires K8s 1.34+).
183+
// +kubebuilder:validation:Required
184+
// +kubebuilder:validation:MinLength=1
185+
Image string `json:"image"`
186+
187+
// paths restricts which directories from the image are mounted.
188+
// Each path is mounted as a separate subPath volumeMount into the agent's
189+
// skills directory. The last segment of each path becomes the mount name
190+
// (e.g., "/skills/prometheus" mounts as "prometheus").
191+
//
192+
// When omitted, the entire image is mounted as a single volume.
193+
// +optional
194+
Paths []string `json:"paths,omitempty"`
195+
}
196+
197+
// AgentSpec defines the desired state of Agent.
198+
type AgentSpec struct {
199+
// llmRef references a cluster-scoped LlmProvider CR that supplies the
200+
// LLM backend for this agent. The operator resolves this reference at
201+
// reconcile time and configures the sandbox pod with the provider's
202+
// credentials and model.
203+
// +kubebuilder:validation:Required
204+
LLMRef corev1.LocalObjectReference `json:"llmRef"`
205+
206+
// skills defines one or more OCI images containing skills to mount
207+
// in the agent's sandbox pod. Each entry specifies an image and optionally
208+
// which paths within that image to mount. The operator creates Kubernetes
209+
// image volumes (requires K8s 1.34+) and mounts them into the agent's
210+
// skills directory.
211+
//
212+
// Multiple entries allow composing skills from different images:
213+
//
214+
// skills:
215+
// - image: registry.ci.openshift.org/ocp/5.0:agentic-skills
216+
// paths:
217+
// - /skills/prometheus
218+
// - /skills/cluster-update/update-advisor
219+
// - image: quay.io/my-org/custom-skills:latest
220+
//
221+
// +kubebuilder:validation:MinItems=1
222+
Skills []SkillsSource `json:"skills"`
223+
224+
// mcpServers defines external MCP (Model Context Protocol) servers the
225+
// agent can connect to for additional tools and context beyond its
226+
// built-in skills. Each server is identified by name and URL.
227+
// +optional
228+
// +listType=map
229+
// +listMapKey=name
230+
MCPServers []MCPServerConfig `json:"mcpServers,omitempty"`
231+
232+
// systemPromptRef references a ConfigMap containing the system prompt.
233+
// The ConfigMap must have a key named "prompt" with the prompt text.
234+
// The system prompt shapes the agent's behavior for its role (analysis,
235+
// execution, or verification). When omitted, the agent uses a default
236+
// prompt appropriate for its workflow step.
237+
// +optional
238+
SystemPromptRef *corev1.LocalObjectReference `json:"systemPromptRef,omitempty"`
239+
240+
// outputFields defines additional structured output fields beyond the
241+
// base schema that every agent produces (diagnosis, proposal, RBAC,
242+
// verification plan). Use this to request domain-specific structured
243+
// data from the agent (e.g., an ACS violation ID, affected images).
244+
// Mutually exclusive with rawOutputSchema.
245+
// +optional
246+
// +listType=map
247+
// +listMapKey=name
248+
OutputFields []OutputField `json:"outputFields,omitempty"`
249+
250+
// rawOutputSchema is an escape hatch that replaces the entire output
251+
// schema with a raw JSON Schema object. Use this when outputFields
252+
// cannot express the schema you need (e.g., deeply nested structures,
253+
// conditional fields). Mutually exclusive with outputFields.
254+
// +optional
255+
RawOutputSchema *apiextensionsv1.JSON `json:"rawOutputSchema,omitempty"`
256+
}
257+
258+
// +kubebuilder:object:root=true
259+
// +kubebuilder:resource:scope=Cluster
260+
// +kubebuilder:printcolumn:name="LLM",type=string,JSONPath=`.spec.llmRef.name`
261+
// +kubebuilder:printcolumn:name="Skills Image",type=string,JSONPath=`.spec.skills[0].image`,priority=1
262+
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
263+
264+
// Agent defines a complete agent configuration: which LLM to use, what
265+
// skills to mount, optional MCP servers, and what system prompt to follow.
266+
// It is the second link in the CRD chain (LlmProvider -> Agent -> Workflow
267+
// -> Proposal) and is referenced by Workflow steps via agentRef.
268+
//
269+
// Agent is cluster-scoped. You typically create a few agents with different
270+
// capabilities and assign them to workflow steps. For example, an analysis
271+
// agent might use a capable model with broad diagnostic skills, while an
272+
// execution agent uses a fast model with targeted remediation skills.
273+
//
274+
// Example — an analysis agent with selective skills and a system prompt:
275+
//
276+
// apiVersion: agentic.openshift.io/v1alpha1
277+
// kind: Agent
278+
// metadata:
279+
// name: analyzer
280+
// spec:
281+
// llmRef:
282+
// name: smart
283+
// skills:
284+
// - image: registry.ci.openshift.org/ocp/5.0:agentic-skills
285+
// paths:
286+
// - /skills/prometheus
287+
// - /skills/cluster-ops
288+
// - /skills/rbac-security
289+
// systemPromptRef:
290+
// name: analysis-prompt
291+
//
292+
// Example — an execution agent with a fast model:
293+
//
294+
// apiVersion: agentic.openshift.io/v1alpha1
295+
// kind: Agent
296+
// metadata:
297+
// name: executor
298+
// spec:
299+
// llmRef:
300+
// name: fast
301+
// skills:
302+
// - image: registry.ci.openshift.org/ocp/5.0:agentic-skills
303+
// paths:
304+
// - /skills/cluster-ops
305+
// systemPromptRef:
306+
// name: execution-prompt
307+
//
308+
// Example — an agent with MCP servers for extended tooling:
309+
//
310+
// apiVersion: agentic.openshift.io/v1alpha1
311+
// kind: Agent
312+
// metadata:
313+
// name: analyzer-with-mcp
314+
// spec:
315+
// llmRef:
316+
// name: smart
317+
// skills:
318+
// - image: registry.ci.openshift.org/ocp/5.0:agentic-skills
319+
// mcpServers:
320+
// - name: openshift
321+
// url: https://mcp.openshift-lightspeed.svc:8443/sse
322+
// timeout: 10
323+
// headers:
324+
// - name: Authorization
325+
// valueFrom:
326+
// type: kubernetes
327+
// - name: pagerduty
328+
// url: https://mcp-pagerduty.example.com/sse
329+
// headers:
330+
// - name: X-API-Key
331+
// valueFrom:
332+
// type: secret
333+
// secretRef:
334+
// name: pagerduty-api-key
335+
// systemPromptRef:
336+
// name: analysis-prompt
337+
type Agent struct {
338+
metav1.TypeMeta `json:",inline"`
339+
metav1.ObjectMeta `json:"metadata,omitempty"`
340+
341+
// +kubebuilder:validation:Required
342+
Spec AgentSpec `json:"spec"`
343+
}
344+
345+
// +kubebuilder:object:root=true
346+
347+
// AgentList contains a list of Agent.
348+
type AgentList struct {
349+
metav1.TypeMeta `json:",inline"`
350+
metav1.ListMeta `json:"metadata,omitempty"`
351+
Items []Agent `json:"items"`
352+
}
353+
354+
func init() {
355+
SchemeBuilder.Register(&Agent{}, &AgentList{})
356+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2026.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package v1alpha1 contains API Schema definitions for the agentic v1alpha1 API group
18+
// +kubebuilder:object:generate=true
19+
// +groupName=agentic.openshift.io
20+
package v1alpha1
21+
22+
import (
23+
"k8s.io/apimachinery/pkg/runtime/schema"
24+
"sigs.k8s.io/controller-runtime/pkg/scheme"
25+
)
26+
27+
var (
28+
// GroupVersion is group version used to register these objects
29+
GroupVersion = schema.GroupVersion{Group: "agentic.openshift.io", Version: "v1alpha1"}
30+
31+
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
32+
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33+
34+
// AddToScheme adds the types in this group-version to the given scheme.
35+
AddToScheme = SchemeBuilder.AddToScheme
36+
)

0 commit comments

Comments
 (0)