Skip to content

Commit 7bb8d7d

Browse files
authored
[foundry extension] - service target support for deploy agent (#5882)
* bump dependencies to latest * renames * recordings * wip * Adding deploy support for foundry extension * remove logic to handle multiple definitions * cspell
1 parent d3e9f15 commit 7bb8d7d

File tree

6 files changed

+1623
-97
lines changed

6 files changed

+1623
-97
lines changed

cli/azd/.vscode/cspell-azd-dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ cmdsubst
7878
cognitiveservices
7979
conditionalize
8080
consolesize
81+
containeragent
8182
containerapp
8283
containerapps
8384
containerd

cli/azd/extensions/azure.foundry.ai.agents/internal/cmd/listen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func newListenCommand() *cobra.Command {
2828

2929
provider := project.NewAgentServiceTargetProvider(azdClient)
3030
host := azdext.NewExtensionHost(azdClient).
31-
WithServiceTarget("foundry.hostedagent", provider)
31+
WithServiceTarget("foundry.containeragent", provider)
3232

3333
// Start listening for events
3434
// This is a blocking call and will not return until the server connection is closed.
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package project
5+
6+
import (
7+
"archive/tar"
8+
"bytes"
9+
"fmt"
10+
"io"
11+
"os"
12+
"path/filepath"
13+
"strings"
14+
"time"
15+
16+
"github.com/braydonk/yaml"
17+
)
18+
19+
// agentYAMLConfig represents the structure of the agent YAML configuration file
20+
type agentYAMLConfig struct {
21+
ID string `yaml:"id"`
22+
Version string `yaml:"version"`
23+
Name string `yaml:"name"`
24+
Description string `yaml:"description"`
25+
Model string `yaml:"model"`
26+
Instructions string `yaml:"instructions"`
27+
Metadata map[string]interface{} `yaml:"metadata"`
28+
}
29+
30+
// parseAgentYAML parses the agent YAML file and returns the configuration
31+
func parseAgentYAML(yamlFilePath string) (*agentYAMLConfig, error) {
32+
// Read the YAML file
33+
data, err := os.ReadFile(yamlFilePath)
34+
if err != nil {
35+
return nil, fmt.Errorf("failed to read YAML file: %w", err)
36+
}
37+
38+
// Parse YAML
39+
var agentConfig agentYAMLConfig
40+
if err := yaml.Unmarshal(data, &agentConfig); err != nil {
41+
return nil, fmt.Errorf("failed to parse YAML: %w", err)
42+
}
43+
44+
// Validate required fields
45+
if agentConfig.ID == "" {
46+
return nil, fmt.Errorf("agent ID is required in YAML file")
47+
}
48+
49+
return &agentConfig, nil
50+
}
51+
52+
func createBuildContext(dockerfilePath string) ([]byte, error) {
53+
var buf bytes.Buffer
54+
tw := tar.NewWriter(&buf)
55+
defer tw.Close()
56+
57+
// Get the directory containing the Dockerfile
58+
dockerfileDir := filepath.Dir(dockerfilePath)
59+
60+
// Walk through the directory and add files to tar
61+
err := filepath.Walk(dockerfileDir, func(path string, info os.FileInfo, err error) error {
62+
if err != nil {
63+
return err
64+
}
65+
66+
// Skip directories
67+
if info.IsDir() {
68+
return nil
69+
}
70+
71+
// Get relative path from dockerfile directory
72+
relPath, err := filepath.Rel(dockerfileDir, path)
73+
if err != nil {
74+
return err
75+
}
76+
77+
// Convert Windows path separators to Unix style for tar
78+
relPath = filepath.ToSlash(relPath)
79+
80+
// Create tar header
81+
header := &tar.Header{
82+
Name: relPath,
83+
Size: info.Size(),
84+
Mode: int64(info.Mode()),
85+
ModTime: info.ModTime(),
86+
}
87+
88+
// Write header
89+
if err := tw.WriteHeader(header); err != nil {
90+
return err
91+
}
92+
93+
// Read and write file content
94+
file, err := os.Open(path)
95+
if err != nil {
96+
return err
97+
}
98+
defer file.Close()
99+
100+
_, err = io.Copy(tw, file)
101+
return err
102+
})
103+
104+
if err != nil {
105+
return nil, fmt.Errorf("failed to create tar archive: %w", err)
106+
}
107+
108+
return buf.Bytes(), nil
109+
}
110+
111+
// generateImageNamesFromAgent generates image names using the agent ID from YAML config
112+
func generateImageNamesFromAgent(agentConfig *agentYAMLConfig, customVersion string) []string {
113+
// Use agent ID as the base image name
114+
imageName := strings.ToLower(strings.ReplaceAll(agentConfig.ID, "_", "-"))
115+
116+
// Use custom version if provided, otherwise use timestamp
117+
var version string
118+
if customVersion != "" {
119+
version = customVersion
120+
} else {
121+
version = time.Now().Format("20060102-150405")
122+
}
123+
124+
// Return array with only the version tag (no latest tag)
125+
return []string{
126+
fmt.Sprintf("%s:%s", imageName, version),
127+
}
128+
}
129+
130+
// ACRTaskRun represents the request body for starting an ACR task run
131+
type ACRTaskRun struct {
132+
Type string `json:"type"`
133+
IsArchive bool `json:"isArchiveEnabled"`
134+
SourceLocation string `json:"sourceLocation"`
135+
DockerFilePath string `json:"dockerFilePath"`
136+
ImageNames []string `json:"imageNames"`
137+
IsPushEnabled bool `json:"isPushEnabled"`
138+
Platform Platform `json:"platform"`
139+
}
140+
141+
type Platform struct {
142+
OS string `json:"os"`
143+
Architecture string `json:"architecture"`
144+
}
145+
146+
// ACRRunResponse represents the response from starting an ACR task run
147+
type ACRRunResponse struct {
148+
RunID string `json:"runId"`
149+
Status string `json:"status"`
150+
}
151+
152+
// ACRRunStatus represents the status response for a run
153+
type ACRRunStatus struct {
154+
RunID string `json:"runId"`
155+
Status string `json:"status"`
156+
StartTime time.Time `json:"startTime"`
157+
EndTime time.Time `json:"endTime,omitempty"`
158+
}

0 commit comments

Comments
 (0)