Skip to content
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
104cd35
Add azure.ai.dataset extension as a standalone extension
m7md7sien Aug 9, 2026
7b4b19e
Put every message the CLI shows in one file
m7md7sien Aug 9, 2026
f5d55a2
Say what this extension is, and show what the service returns
m7md7sien Aug 9, 2026
93516a2
Stop the same panic and leak here
m7md7sien Aug 9, 2026
2e8f8fb
Say when the answer was 'log in again' here too
m7md7sien Aug 9, 2026
88ffc5f
Tell people to retry before telling them to log in
m7md7sien Aug 9, 2026
dd0b293
Refuse an empty dataset early, bound the blob calls, guard the nil list
m7md7sien Aug 9, 2026
5c5084b
Do not upload the byte order mark Windows wrote
m7md7sien Aug 10, 2026
2e7b315
Say a name is not a dataset, and refuse one the service will not take
m7md7sien Aug 10, 2026
9df2b57
Format the Go sources with gofmt
m7md7sien Aug 10, 2026
be18334
Say a --from-file path is missing, not which syscall found out
m7md7sien Aug 10, 2026
bbc9c89
Upload the file that was named, not its neighbour
m7md7sien Aug 10, 2026
347fefb
Add a live CLI suite for the dataset extension
m7md7sien Aug 10, 2026
fef40d1
Find gotestsum on the shell Windows actually runs
m7md7sien Aug 10, 2026
5dabe21
Refuse to guess which dataset a folder meant, and keep JSON parseable
m7md7sien Aug 10, 2026
a986da1
Answer an unknown name the way its sibling does
m7md7sien Aug 10, 2026
69e7031
Refuse a malformed row and a malformed name before they reach the ser…
m7md7sien Aug 11, 2026
8e9e3fb
Release 1.0.0-beta.2
m7md7sien Aug 11, 2026
897c63f
Print a path someone can copy back
m7md7sien Aug 11, 2026
4cfa842
Release 1.0.0-beta.3
m7md7sien Aug 11, 2026
3a9d92f
Name the dataset when listing its versions finds none
m7md7sien Aug 11, 2026
7ceef44
Bump version.txt with extension.yaml, and check that they agree
m7md7sien Aug 11, 2026
454e1f3
Probe the version a create actually publishes
m7md7sien Aug 11, 2026
6e7aacf
Give the release its changelog entry back
m7md7sien Aug 12, 2026
7acb239
Take go fix's loop rewrite, so the check has nothing left to report
m7md7sien Aug 12, 2026
0efc7b5
Fix lint findings in azure.ai.dataset
m7md7sien Aug 13, 2026
5bd8aa9
Stop logging storage SAS credentials in azure.ai.dataset
m7md7sien Aug 13, 2026
94424e0
Fix cspell failures on the dataset extension
m7md7sien Aug 13, 2026
d87b99f
Stop silent failures in dataset version selection
m7md7sien Aug 13, 2026
aab6855
Follow nextLink when listing datasets and versions
m7md7sien Aug 13, 2026
24b7c74
Fix pagination edge cases and simplify the changelog
m7md7sien Aug 13, 2026
3faf386
Bump to 1.0.0-beta.6
m7md7sien Aug 13, 2026
289aacb
Pin the two paging cases a fork found weak in the sibling paginator
m7md7sien Aug 13, 2026
1c9ca30
Refuse an update only when the service confirms the name is unknown
m7md7sien Aug 16, 2026
35cbaec
Stamp the version with this module's path, not the eval extension's
m7md7sien Aug 16, 2026
0ff82f2
Match the extension's case-insensitive .jsonl scan in the API layer
m7md7sien Aug 16, 2026
04af404
Correct the generation pointer, the version hint, and --version on cr…
m7md7sien Aug 16, 2026
8b8b8af
Use strings.SplitSeq in the stamping test, as go fix prefers
m7md7sien Aug 16, 2026
048b43d
Follow the NextMarker when listing container blobs
m7md7sien Aug 16, 2026
29f83f7
Carry the gosec suppression that goes with the ported blob listing
m7md7sien Aug 16, 2026
2a2ce2f
Share the redaction helpers and give paging its own file, as the eval…
m7md7sien Aug 16, 2026
542fc74
Add urlsafe to the word list, as the eval copy already has
m7md7sien Aug 16, 2026
98a320b
Name the path the caller gave when a read fails
m7md7sien Aug 16, 2026
fddc3df
Redact the SAS when a request cannot even be built
m7md7sien Aug 16, 2026
df07a49
Give this extension the token retry the other one already had
m7md7sien Aug 16, 2026
bdcaa19
Fix the endpoint cascade, and cover the existence probe
m7md7sien Aug 17, 2026
830bfc9
Satisfy the linters the first push tripped
m7md7sien Aug 17, 2026
5a383e9
Name the codes that mean absence, rather than the ones that do not
m7md7sien Aug 17, 2026
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
22 changes: 22 additions & 0 deletions .github/workflows/lint-ext-azure-ai-dataset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: ext-azure-ai-dataset-ci

on:
pull_request:
paths:
- "cli/azd/extensions/azure.ai.dataset/**"
- ".github/workflows/lint-ext-azure-ai-dataset.yml"
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write # required by reusable workflow lint-go.yml

jobs:
lint:
uses: ./.github/workflows/lint-go.yml
with:
working-directory: cli/azd/extensions/azure.ai.dataset
3 changes: 3 additions & 0 deletions cli/azd/.vscode/cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@ overrides:
words:
- forbidigo
- Logf
- filename: extensions/azure.ai.dataset/README.md
words:
- CODEOWNERS
- filename: .golangci.yaml
words:
- forbidigo
Expand Down
5 changes: 5 additions & 0 deletions cli/azd/extensions/azure.ai.dataset/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Test report written by ci-test.ps1 for the pipeline to publish.
junitTestReport.xml

# Debug log written when --debug or AZD_EXT_DEBUG is set.
azd-ai-dataset-*.log
21 changes: 21 additions & 0 deletions cli/azd/extensions/azure.ai.dataset/.golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: "2"

linters:
default: none
enable:
- gosec
- lll
- unused
- errorlint
settings:
lll:
line-length: 220
tab-width: 4
gosec:
excludes:
- G204 # Subprocess launched with variable (bicep build invoked in tests)
- G304 # Potential file inclusion via variable

formatters:
enable:
- gofmt
18 changes: 18 additions & 0 deletions cli/azd/extensions/azure.ai.dataset/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Release History

## 1.0.0-beta.6 (Unreleased)

First release of the Foundry datasets extension.

### Features Added

- Register and manage Foundry datasets from the terminal: `create`, `update`,
`list`, `show`, `delete`, and `versions list`.
- Publishes a local `.jsonl` file or folder as a versioned dataset, picking the
next version from what the project already carries.
- Validates before sending: a malformed row, an empty dataset, a name the
service will not take, and a folder that could mean more than one dataset are
each refused locally.
- Reads dataset content back, whether the service hands out a blob URI or the
container holding it.
- `-o json` on every command, and `--no-prompt` for unattended use.
63 changes: 63 additions & 0 deletions cli/azd/extensions/azure.ai.dataset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Foundry datasets (Beta)

Register and version Foundry datasets from your terminal.

```console
$ azd extension install azure.ai.dataset
$ azd ai dataset --help
```

A dataset is a general Foundry asset: evaluation needs one, and so do
fine-tuning and other scenarios. That is why these commands live here rather
than inside `azure.ai.evaluations`.

## Commands

| Command | What it does |
|---|---|
| `azd ai dataset create <name> --from-file <path>` | Register a dataset, publishing its first version |
| `azd ai dataset update <name> --from-file <path>` | Publish a further version |
| `azd ai dataset list` | List the project's datasets |
| `azd ai dataset show <name>` | Show one dataset |
| `azd ai dataset delete <name>` | Delete a dataset version |
| `azd ai dataset versions list <name>` | List a dataset's versions |

## Generating a dataset

Generation is `azd ai eval generate`, in `azure.ai.evaluations`, and stays
there: it writes the `datasets:` entry in `evals/azure.eval.yaml`, which is that
extension's file. Splitting the two would leave a generated dataset registered
with the service but absent from the configuration, so `azd up` would not
reconcile it and no eval could name it.

Once a file exists, `create` registers it here.

## Project endpoint

Every command resolves the Foundry project endpoint in this order:

1. `--project-endpoint`
2. `FOUNDRY_PROJECT_ENDPOINT` in the active azd environment
3. the host environment variable of the same name
Comment thread
m7md7sien marked this conversation as resolved.
Outdated

## Building

```console
$ go build ./...
$ go test ./...
```

## TODO before release

Both are files the azd extensions team owns, so they are not changed here:

- [ ] **`cli/azd/extensions/registry.json`** — add the `azure.ai.dataset` entry.
Until it exists `azd extension install azure.ai.dataset` cannot resolve, so
the extension is only reachable through `azd x pack` + `azd x publish` into
the local source registry.
- [ ] **`.github/CODEOWNERS`** — add `/cli/azd/extensions/azure.ai.dataset/`.
Every sibling Foundry extension has an entry; without one, PRs here get no
reviewer routing.
- [ ] **`microsoft.foundry/extension.yaml`** — add the dependency, but only
after the registry entry lands. Declaring a dependency that cannot resolve
breaks installing the bundle.
78 changes: 78 additions & 0 deletions cli/azd/extensions/azure.ai.dataset/build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Ensure script fails on any error
$ErrorActionPreference = 'Stop'

# Get the directory of the script
$EXTENSION_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path

# Change to the script directory
Set-Location -Path $EXTENSION_DIR

# Create a safe version of EXTENSION_ID replacing dots with dashes
$EXTENSION_ID_SAFE = $env:EXTENSION_ID -replace '\.', '-'

# Define output directory
$OUTPUT_DIR = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $EXTENSION_DIR "bin" }

# Create output directory if it doesn't exist
if (-not (Test-Path -Path $OUTPUT_DIR)) {
New-Item -ItemType Directory -Path $OUTPUT_DIR | Out-Null
}

# Get Git commit hash and build date
$COMMIT = git rev-parse HEAD
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Failed to get git commit hash"
exit 1
}
$BUILD_DATE = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")

# List of OS and architecture combinations
if ($env:EXTENSION_PLATFORM) {
$PLATFORMS = @($env:EXTENSION_PLATFORM)
}
else {
$PLATFORMS = @(
"windows/amd64",
"windows/arm64",
"darwin/amd64",
"darwin/arm64",
"linux/amd64",
"linux/arm64"
)
}

$VERSION_PATH = "azureaidataset/internal/version"

# Loop through platforms and build
foreach ($PLATFORM in $PLATFORMS) {
$OS, $ARCH = $PLATFORM -split '/'

$OUTPUT_NAME = Join-Path $OUTPUT_DIR "$EXTENSION_ID_SAFE-$OS-$ARCH"

if ($OS -eq "windows") {
$OUTPUT_NAME += ".exe"
}

Write-Host "Building for $OS/$ARCH..."

# Delete the output file if it already exists
if (Test-Path -Path $OUTPUT_NAME) {
Remove-Item -Path $OUTPUT_NAME -Force
}

# Set environment variables for Go build
$env:GOOS = $OS
$env:GOARCH = $ARCH

go build `
-ldflags="-X '$VERSION_PATH.Version=$env:EXTENSION_VERSION' -X '$VERSION_PATH.Commit=$COMMIT' -X '$VERSION_PATH.BuildDate=$BUILD_DATE'" `
-o $OUTPUT_NAME

if ($LASTEXITCODE -ne 0) {
Write-Host "An error occurred while building for $OS/$ARCH"
exit 1
}
}

Write-Host "Build completed successfully!"
Write-Host "Binaries are located in the $OUTPUT_DIR directory."
66 changes: 66 additions & 0 deletions cli/azd/extensions/azure.ai.dataset/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/bin/bash

# Get the directory of the script
EXTENSION_DIR="$(cd "$(dirname "$0")" && pwd)"

# Change to the script directory
cd "$EXTENSION_DIR" || exit

# Create a safe version of EXTENSION_ID replacing dots with dashes
EXTENSION_ID_SAFE="${EXTENSION_ID//./-}"

# Define output directory
OUTPUT_DIR="${OUTPUT_DIR:-$EXTENSION_DIR/bin}"

# Create output and target directories if they don't exist
mkdir -p "$OUTPUT_DIR"

# Get Git commit hash and build date
COMMIT=$(git rev-parse HEAD)
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

# List of OS and architecture combinations
if [ -n "$EXTENSION_PLATFORM" ]; then
PLATFORMS=("$EXTENSION_PLATFORM")
else
PLATFORMS=(
"windows/amd64"
"windows/arm64"
"darwin/amd64"
"darwin/arm64"
"linux/amd64"
"linux/arm64"
)
fi

VERSION_PATH="azureaidataset/internal/version"

# Loop through platforms and build
for PLATFORM in "${PLATFORMS[@]}"; do
OS=$(echo "$PLATFORM" | cut -d'/' -f1)
ARCH=$(echo "$PLATFORM" | cut -d'/' -f2)

OUTPUT_NAME="$OUTPUT_DIR/$EXTENSION_ID_SAFE-$OS-$ARCH"

if [ "$OS" = "windows" ]; then
OUTPUT_NAME+='.exe'
fi

echo "Building for $OS/$ARCH..."

# Delete the output file if it already exists
[ -f "$OUTPUT_NAME" ] && rm -f "$OUTPUT_NAME"

# Set environment variables for Go build
GOOS=$OS GOARCH=$ARCH go build \
-ldflags="-X '$VERSION_PATH.Version=$EXTENSION_VERSION' -X '$VERSION_PATH.Commit=$COMMIT' -X '$VERSION_PATH.BuildDate=$BUILD_DATE'" \
-o "$OUTPUT_NAME"

if [ $? -ne 0 ]; then
echo "An error occurred while building for $OS/$ARCH"
exit 1
fi
done

echo "Build completed successfully!"
echo "Binaries are located in the $OUTPUT_DIR directory."
Loading
Loading