Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/spf13/cobra"
)

const delegatedSchemaVersion = 1
const delegatedSchemaVersion = 2

const (
projectInitSourceAgents = "azure.ai.agents/init"
Expand All @@ -27,6 +27,7 @@ const (
type delegatedProject struct {
ResourceID string `json:"resourceId,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
Name string `json:"name,omitempty"`
}

type delegatedInfra struct {
Expand All @@ -37,6 +38,23 @@ type delegatedRequirements struct {
AllowedLocations []string `json:"allowedLocations,omitempty"`
}

type delegatedDeployment struct {
Name string `json:"name"`
Model delegatedDeploymentModel `json:"model"`
SKU delegatedDeploymentSKU `json:"sku"`
}

type delegatedDeploymentModel struct {
Format string `json:"format"`
Name string `json:"name"`
Version string `json:"version"`
}

type delegatedDeploymentSKU struct {
Name string `json:"name"`
Capacity int `json:"capacity"`
}

// projectInitRequest is the versioned IPC contract for agents.
type projectInitRequest struct {
SchemaVersion int `json:"schemaVersion"`
Expand All @@ -47,11 +65,17 @@ type projectInitRequest struct {
Requirements delegatedRequirements `json:"requirements"`
ResolveAzureContext bool `json:"resolveAzureContext"`
Force bool `json:"force"`
ReplaceDeployments bool `json:"replaceDeployments,omitempty"`
Deployments []delegatedDeployment `json:"deployments,omitempty"`
}

type delegatedModel struct {
Name string `json:"name"`
DeploymentName string `json:"deploymentName,omitempty"`
Format string `json:"format,omitempty"`
Version string `json:"version,omitempty"`
SKU string `json:"sku,omitempty"`
Capacity int32 `json:"capacity,omitempty"`
RequiredCapabilities []string `json:"requiredCapabilities,omitempty"`
AllowedLocations []string `json:"allowedLocations,omitempty"`
ExcludedModelNames []string `json:"excludedModelNames,omitempty"`
Expand Down Expand Up @@ -129,6 +153,29 @@ func (r *projectInitRequest) validate() error {
return contractValidationError("requirements.allowedLocations must contain a location")
}
r.Requirements.AllowedLocations = locations
if len(r.Deployments) > 0 && !r.ReplaceDeployments {
return contractValidationError(
"deployments requires replaceDeployments",
)
}
if r.ReplaceDeployments {
seen := make(map[string]struct{}, len(r.Deployments))
for _, deployment := range r.Deployments {
name := strings.TrimSpace(deployment.Name)
if name == "" {
return contractValidationError(
"deployment names cannot be empty",
)
}
key := strings.ToLower(name)
if _, exists := seen[key]; exists {
return contractValidationError(
fmt.Sprintf("deployment %q is duplicated", name),
)
}
seen[key] = struct{}{}
}
}
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,21 @@ func selectModelDeployment(
noPrompt bool,
) (*selectedDeployment, error) {
modelFormat, modelName := splitModelReference(model.Name)
if model.Format != "" {
modelFormat = model.Format
}
if modelName == "" {
return nil, contractValidationError("model.name is required")
}
if model.Version != "" {
selection.Version = model.Version
}
if model.SKU != "" {
selection.SKU = model.SKU
}
if model.Capacity > 0 {
selection.Capacity = model.Capacity
}

locations, err := deploymentLocations(
model.AllowedLocations,
Expand Down Expand Up @@ -183,7 +195,7 @@ func selectModelDeployment(
if location == "" && len(locations) == 1 {
location = locations[0]
}
return &selectedDeployment{
selected := &selectedDeployment{
Deployment: synthesis.Deployment{
Name: chooseDeploymentName(model.DeploymentName, candidate.GetModelName()),
Model: synthesis.DeploymentModel{
Expand All @@ -197,7 +209,50 @@ func selectModelDeployment(
},
},
Location: location,
}, nil
}
if model.Format != "" &&
!strings.EqualFold(selected.Deployment.Model.Format, model.Format) {
return nil, exterrors.Validation(
"model_deployment_unavailable",
fmt.Sprintf(
"model %q does not have the requested format %q",
modelName, model.Format,
),
"specify a deployment format supported by the selected model",
)
}
if model.Version != "" && selected.Deployment.Model.Version != model.Version {
return nil, exterrors.Validation(
"model_deployment_unavailable",
fmt.Sprintf(
"model %q does not have the requested version %q",
modelName, model.Version,
),
"specify a deployment version supported by the selected model",
)
}
if model.SKU != "" &&
!strings.EqualFold(selected.Deployment.Sku.Name, model.SKU) {
return nil, exterrors.Validation(
"model_deployment_unavailable",
fmt.Sprintf(
"model %q does not have the requested SKU %q",
modelName, model.SKU,
),
"specify a deployment SKU supported by the selected model",
)
}
if model.Capacity > 0 && selected.Deployment.Sku.Capacity != int(model.Capacity) {
return nil, exterrors.Validation(
"model_deployment_unavailable",
fmt.Sprintf(
"model %q does not have the requested capacity %d",
modelName, model.Capacity,
),
"specify a deployment capacity supported by the selected model",
)
}
return selected, nil
}

func deploymentLocations(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ func (a *ProjectDeploymentAddAction) Run(ctx context.Context) error {
if err != nil {
return err
}
if values["AZURE_AI_PROJECT_ID"] == "" {
if values["AZURE_AI_PROJECT_ID"] == "" &&
serviceEndpoint(service.Resolved) != "" {
return exterrors.Validation(
"project_deployment_requires_id",
"managed model deployments require an existing Foundry project resource ID",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func planProjectEnvironment(
sets[key] = value
}
}
// Do not overwrite a preselected location when ARM omits one.
if project.Location != "" {
// Keep the resource-group location selected by the user.
if project.Location != "" && oldValues["AZURE_LOCATION"] == "" {
sets["AZURE_LOCATION"] = project.Location
}
case projectModeExistingEndpoint:
Expand Down
Loading