Skip to content
opendatahub-ioPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

mlflow-go

A Go SDK for MLflow. Supports Experiment Tracking, the Prompt Registry, and the MCP Registry.

Features

Experiment Tracking

  • Create, get, update, and delete experiments
  • Create, get, update, and delete runs
  • Log metrics (single and batch), parameters, and tags
  • Search experiments and runs with filter expressions
  • Typed run status constants and view type filters

Prompt Registry

  • Load prompts by name (latest or specific version)
  • List prompts and versions with filtering and pagination
  • Register text prompts and chat prompts (with model configuration)
  • Delete prompts, versions, and tags
  • Format prompts with variable substitution
  • Modify prompts locally with immutable operations

MCP Registry

  • Register, get, update, delete, and search MCP (Model Context Protocol) servers
  • Create, get, update, delete, and search server versions, each with its own server.json, discovered tools, and per-connection-mode display settings (connect_options)
  • Create, get, update, search, and delete access endpoints (URLs bound to a server, version, or alias)
  • Set/delete tags on servers and versions
  • Set/get/delete aliases (e.g. "production") pointing at a version

Workspace Isolation (Midstream)

  • Forward custom headers on every request via WithHeaders
  • Tenant isolation with X-MLFLOW-WORKSPACE header
  • Compatible with the Red Hat midstream fork (opendatahub-io/mlflow)

General

  • Full context support for cancellation and timeouts
  • Structured logging with slog.Handler
  • Type-safe error handling

Installation

go get github.com/opendatahub-io/mlflow-go

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/opendatahub-io/mlflow-go/mlflow"
)

func main() {
    // Create client (reads MLFLOW_TRACKING_URI from environment)
    client, err := mlflow.NewClient()
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()

    // Load a prompt
    prompt, err := client.PromptRegistry().LoadPrompt(ctx, "my-prompt")
    if err != nil {
        if mlflow.IsNotFound(err) {
            log.Fatal("Prompt not found")
        }
        log.Fatal(err)
    }

    fmt.Printf("Loaded: %s v%d\n", prompt.Name, prompt.Version)
    fmt.Printf("Template: %s\n", prompt.Template)
}

Configuration

Environment Variables

Variable Description Required
MLFLOW_TRACKING_URI MLflow server URL Yes
MLFLOW_INSECURE_SKIP_TLS_VERIFY Allow HTTP (set to true or 1) No

Explicit Configuration

client, err := mlflow.NewClient(
    mlflow.WithTrackingURI("https://mlflow.example.com"),
    mlflow.WithHeaders(map[string]string{
        "Authorization": "Bearer my-token",
    }),
    mlflow.WithTimeout(60 * time.Second),
)

Custom Headers and Workspace Isolation

WithHeaders forwards custom HTTP headers on every API request. This is primarily used for workspace-based tenant isolation with the Red Hat midstream fork (opendatahub-io/mlflow), but can also carry additional auth headers or routing metadata.

The midstream fork adds multi-tenant workspace support to MLflow. Each workspace acts as an isolated namespace — prompts, models, and versions created in one workspace are completely invisible from another. The server routes requests to the correct workspace based on the X-MLFLOW-WORKSPACE header.

Note: Workspace isolation is currently available in the Red Hat midstream fork. This feature is planned to be upstreamed to MLflow in the near future.

// Create a client scoped to a workspace
clientA, err := mlflow.NewClient(
    mlflow.WithTrackingURI("http://127.0.0.1:5000"),
    mlflow.WithInsecure(),
    mlflow.WithHeaders(map[string]string{
        "X-MLFLOW-WORKSPACE": "team-bella",
    }),
)

// Register a prompt in team-bella
clientA.PromptRegistry().RegisterPrompt(ctx, "my-prompt", "Hello {{name}}!")

// Create a second client scoped to team-dora
clientB, err := mlflow.NewClient(
    mlflow.WithTrackingURI("http://127.0.0.1:5000"),
    mlflow.WithInsecure(),
    mlflow.WithHeaders(map[string]string{
        "X-MLFLOW-WORKSPACE": "team-dora",
    }),
)

// This returns NotFound — the prompt exists only in team-bella
_, err = clientB.PromptRegistry().LoadPrompt(ctx, "my-prompt")
// mlflow.IsNotFound(err) == true

Workspaces must be pre-created on the server before use. If you reference a workspace that doesn't exist, the server returns a RESOURCE_DOES_NOT_EXIST error. For local development:

# Start midstream server with workspaces enabled
make dev/up-midstream

# Create workspaces
make dev/seed-workspaces

Local Development

// Allow HTTP for local development
client, err := mlflow.NewClient(
    mlflow.WithTrackingURI("http://localhost:5000"),
    mlflow.WithInsecure(),
)

Experiment Tracking

Create an Experiment and Log a Run

import "github.com/opendatahub-io/mlflow-go/mlflow/tracking"

ctx := context.Background()

// Create an experiment with a kind (controls how the MLflow UI displays it)
expID, err := client.Tracking().CreateExperiment(ctx, "my-experiment",
    tracking.WithExperimentKind(tracking.ExperimentKindMLDevelopment),
)

// Create a run in the experiment
run, err := client.Tracking().CreateRun(ctx, expID,
    tracking.WithRunName("training-run-1"),
    tracking.WithRunTags(map[string]string{"model": "sklearn"}),
)
runID := run.Info.RunID

// Log metrics, params, and tags
err = client.Tracking().LogMetric(ctx, runID, "rmse", 0.85, tracking.WithStep(1))
err = client.Tracking().LogParam(ctx, runID, "learning_rate", "0.01")
err = client.Tracking().SetTag(ctx, runID, "status", "training")

// Mark run as finished
info, err := client.Tracking().UpdateRun(ctx, runID,
    tracking.WithStatus(tracking.RunStatusFinished),
    tracking.WithEndTime(time.Now()),
)

Batch Logging

err := client.Tracking().LogBatch(ctx, runID,
    []tracking.Metric{
        {Key: "loss", Value: 0.5, Step: 1},
        {Key: "loss", Value: 0.3, Step: 2},
        {Key: "accuracy", Value: 0.92, Step: 1},
    },
    []tracking.Param{
        {Key: "optimizer", Value: "adam"},
        {Key: "batch_size", Value: "32"},
    },
    map[string]string{
        "framework": "pytorch",
    },
)

List All Experiments

// List experiments (default: active only; use WithExperimentsViewType for deleted/all)
experiments, err := client.Tracking().SearchExperiments(ctx)
for _, e := range experiments.Experiments {
    fmt.Printf("[%s] %s (lifecycle: %s)\n", e.ID, e.Name, e.LifecycleStage)
}

Search Experiments and Runs

// Search experiments by name
experiments, err := client.Tracking().SearchExperiments(ctx,
    tracking.WithExperimentsFilter("name = 'my-experiment'"),
)

// Search runs with metric filter
runs, err := client.Tracking().SearchRuns(ctx, []string{expID},
    tracking.WithRunsFilter("metrics.rmse < 1"),
    tracking.WithRunsOrderBy("metrics.rmse ASC"),
    tracking.WithRunsMaxResults(10),
)
for _, r := range runs.Runs {
    fmt.Printf("Run %s: status=%s\n", r.Info.RunID, r.Info.Status)
}

Get and Delete

// Get experiment by ID or name
exp, err := client.Tracking().GetExperiment(ctx, expID)
exp, err = client.Tracking().GetExperimentByName(ctx, "my-experiment")

// Get run
run, err := client.Tracking().GetRun(ctx, runID)

// Delete tag, run, experiment
err = client.Tracking().DeleteTag(ctx, runID, "status")
err = client.Tracking().DeleteRun(ctx, runID)
err = client.Tracking().DeleteExperiment(ctx, expID)

Artifacts

Upload, list, and download run artifacts via client.Artifacts(). ListArtifacts uses the standard tracking API (GET /api/2.0/mlflow/artifacts/list). For upload and download, local filesystem artifact roots (file:// or path-only) use tracking-server routes directly; otherwise the client tries presigned URLs first (cloud-backed stores), then the mlflow-artifacts proxy when the run uses mlflow-artifacts:/ URIs.

import (
    "bytes"
    "io"
    "log"
    "github.com/opendatahub-io/mlflow-go/mlflow/artifacts"
)

// Upload an artifact file
err := client.Artifacts().LogArtifact(ctx, runID, "metrics/output.txt",
    bytes.NewReader([]byte("accuracy=0.95")),
    artifacts.WithContentType("text/plain"),
)

// List artifacts (optionally filter by path prefix)
list, err := client.Artifacts().ListArtifacts(ctx, runID, artifacts.WithArtifactPath("metrics"))

// Download an artifact file (caller must close the returned reader)
rc, err := client.Artifacts().DownloadArtifact(ctx, runID, "metrics/output.txt")
if err != nil {
    log.Fatal(err)
}
defer rc.Close()
data, err := io.ReadAll(rc)
if err != nil {
    log.Fatal(err)
}

Path-based artifacts (no run ID)

UploadArtifact and DownloadArtifactByPath transfer an object addressed by a storage path directly through the mlflow-artifacts proxy (PUT/GET /api/2.0/mlflow-artifacts/artifacts/{path}), without a run ID. They are meant for large models and datasets: the object is streamed in both directions with no size cap and without buffering the whole thing in memory.

// Upload by storage path (streamed, no size limit; caller keeps ownership of the reader)
err := client.Artifacts().UploadArtifact(ctx, "experiments/1/model.bin",
    reader, // any io.Reader (*os.File, *bytes.Reader, ...)
    artifacts.WithUploadContentType("application/octet-stream"), // optional
)

// Download by storage path (caller must close the returned reader)
rc, err := client.Artifacts().DownloadArtifactByPath(ctx, "experiments/1/model.bin")
if err != nil {
    log.Fatal(err)
}
defer rc.Close()

Because these transfers can be arbitrarily large, they are not bounded by the client WithTimeout; bound them with a context deadline. When the context has no deadline, only the wait for response headers is bounded, by mlflow.WithStreamHeaderTimeout (default 5 minutes; a negative value disables the fallback).

For local development, start MLflow with artifact serving enabled (make dev/up configures this automatically):

mlflow server \
  --serve-artifacts \
  --artifacts-destination ./mlartifacts

Alternatively, use direct filesystem mode with --no-serve-artifacts and a filesystem --default-artifact-root; the client uses tracking-server upload/download routes in that case.

Presigned upload requires MLflow 3.12+ with a cloud-backed artifact store (S3, GCS, etc.).

View Types

Use typed constants to filter by lifecycle stage:

// Search only deleted experiments
experiments, err := client.Tracking().SearchExperiments(ctx,
    tracking.WithExperimentsViewType(tracking.ViewTypeDeletedOnly),
)

// Search all runs (active + deleted)
runs, err := client.Tracking().SearchRuns(ctx, []string{expID},
    tracking.WithRunsViewType(tracking.ViewTypeAll),
)

Experiment Kinds

Set the experiment kind at creation time to control how the MLflow UI displays the experiment. If not set, the UI will prompt users to select a type.

Constant Value Use case
ExperimentKindMLDevelopment custom_model_development Traditional ML model training
ExperimentKindGenAIDevelopment genai_development GenAI / LLM development
ExperimentKindFineTuning finetuning Model fine-tuning
ExperimentKindForecasting forecasting Time series forecasting
ExperimentKindClassification classification Classification tasks
ExperimentKindRegression regression Regression tasks
ExperimentKindAutoML automl Automated ML
expID, err := client.Tracking().CreateExperiment(ctx, "my-genai-experiment",
    tracking.WithExperimentKind(tracking.ExperimentKindGenAIDevelopment),
)

Prompt Registry

Core Types

The SDK uses two main types for prompts:

  • Prompt – Lightweight metadata returned by ListPrompts(). Contains name, description, latest version number, and tags. Does not include template content.
  • PromptVersion – Full prompt content returned by LoadPrompt(). Contains the template (or messages for chat prompts), version number, aliases, model config, and timestamps. This is what you use to format prompts with variables.

Use ListPrompts() to browse available prompts, then LoadPrompt() to fetch the full content when you need it.

Usage Examples

Load a Specific Version

import "github.com/opendatahub-io/mlflow-go/mlflow/promptregistry"

prompt, err := client.PromptRegistry().LoadPrompt(ctx, "my-prompt", promptregistry.WithVersion(2))

Load by Alias

// Load the version pointed to by an alias (e.g., "production", "staging")
prompt, err := client.PromptRegistry().LoadPrompt(ctx, "my-prompt", promptregistry.WithAlias("production"))

Manage Aliases

// Set an alias to point to a specific version
err := client.PromptRegistry().SetPromptAlias(ctx, "my-prompt", "production", 3)

// Delete an alias
err := client.PromptRegistry().DeletePromptAlias(ctx, "my-prompt", "staging")

Delete Prompts and Versions

// Delete a prompt (cascades to delete all versions and aliases on MLflow OSS)
err := client.PromptRegistry().DeletePrompt(ctx, "my-prompt")

// Delete a specific version
err = client.PromptRegistry().DeletePromptVersion(ctx, "my-prompt", 2)
if mlflow.IsAliasConflict(err) {
    // Databricks only: must remove aliases pointing to this version first
    // (MLflow OSS silently removes aliases on version deletion)
    _ = client.PromptRegistry().DeletePromptAlias(ctx, "my-prompt", "production")
    err = client.PromptRegistry().DeletePromptVersion(ctx, "my-prompt", 2)
}

// Delete tags
err = client.PromptRegistry().DeletePromptTag(ctx, "my-prompt", "environment")
err = client.PromptRegistry().DeletePromptVersionTag(ctx, "my-prompt", 1, "reviewed")

List All Prompts

list, err := client.PromptRegistry().ListPrompts(ctx)
if err != nil {
    log.Fatal(err)
}

for _, info := range list.Prompts {
    fmt.Printf("%s (latest: v%d)\n", info.Name, info.LatestVersion)
}

// Pagination: fetch next page if available
if list.NextPageToken != "" {
    nextPage, err := client.PromptRegistry().ListPrompts(ctx, promptregistry.WithPageToken(list.NextPageToken))
    // ...
}

List Prompts with Filters

// Filter by name pattern and tags
list, err := client.PromptRegistry().ListPrompts(ctx,
    promptregistry.WithNameFilter("dog-%"),  // SQL LIKE syntax
    promptregistry.WithTagFilter(map[string]string{"category": "pets"}),
    promptregistry.WithMaxResults(10),
)

List Prompt Versions

versions, err := client.PromptRegistry().ListPromptVersions(ctx, "my-prompt")
if err != nil {
    log.Fatal(err)
}

for _, v := range versions.Versions {
    fmt.Printf("v%d: %s\n", v.Version, v.CommitMessage)
}

// Limit results
versions, err = client.PromptRegistry().ListPromptVersions(ctx, "my-prompt",
    promptregistry.WithVersionsMaxResults(10),
)

MLflow OSS Known Issue: MLflow OSS has a bug where the /model-versions/search endpoint permanently returns empty results for model versions created in rapid succession (<50ms between calls). The data is written correctly (direct GET by version number works), but the search endpoint never indexes those versions. This affects both SQLite and PostgreSQL backends, pointing to a stale SQLAlchemy session issue in MLflow's multi-worker Uvicorn setup rather than a database-level problem.

ListPromptVersions works around this by trying the search endpoint first, and falling back to fetching versions individually (via the @latest alias + direct GET per version) when search returns empty. We plan to report this issue upstream to MLflow.

Register a Text Prompt

prompt, err := client.PromptRegistry().RegisterPrompt(ctx, "dog-walker-prompt",
    "Time to walk Bella and Dora! Meeting at {{location}} at {{time}}.",
    promptregistry.WithCommitMessage("Walk reminder for Bella and Dora"),
    promptregistry.WithTags(map[string]string{
        "dogs": "bella,dora",
        "category": "scheduling",
    }),
)
fmt.Printf("Created: %s v%d\n", prompt.Name, prompt.Version)

Register a Chat Prompt

messages := []promptregistry.ChatMessage{
    {Role: "system", Content: "You are a helpful dog walking assistant for {{owner}}."},
    {Role: "user", Content: "When should I walk {{dog_name}} today?"},
}

// Optional: include model configuration
temp := 0.7
modelConfig := &promptregistry.PromptModelConfig{
    Provider:    "openai",
    ModelName:   "gpt-4",
    Temperature: &temp,
}

prompt, err := client.PromptRegistry().RegisterChatPrompt(ctx, "dog-assistant",
    messages,
    promptregistry.WithCommitMessage("Chat assistant for dog walking"),
    promptregistry.WithModelConfig(modelConfig),
)
fmt.Printf("Created chat prompt: %s v%d\n", prompt.Name, prompt.Version)

Format Prompts with Variables

// Text prompts - get formatted string directly
prompt, _ := client.PromptRegistry().LoadPrompt(ctx, "dog-walker-prompt")
text, err := prompt.FormatAsText(map[string]string{
    "location": "Central Park",
    "time":     "3pm",
})
// Result: "Time to walk Bella and Dora! Meeting at Central Park at 3pm."

// Chat prompts - get formatted messages
chatPrompt, _ := client.PromptRegistry().LoadPrompt(ctx, "dog-assistant")
messages, err := chatPrompt.FormatAsMessages(map[string]string{
    "owner":    "Alice",
    "dog_name": "Bella",
})
// Returns []ChatMessage with variables substituted

// Generic format - returns a new PromptVersion with variables substituted
formatted, err := prompt.Format(map[string]string{"location": "Park", "time": "3pm"})
// formatted.Template or formatted.Messages contains the result

Modify and Create New Version

// Load existing prompt
prompt, err := client.PromptRegistry().LoadPrompt(ctx, "dog-walker-prompt")
if err != nil {
    log.Fatal(err)
}

// Modify locally (original unchanged)
modified := prompt.
    WithTemplate("Hey {{owner}}! Bella and Dora are ready for their walk. Don't forget the treats!").
    WithCommitMessage("Added owner and treats reminder")

// Register as new version
newVersion, err := client.PromptRegistry().RegisterPrompt(ctx, modified.Name, modified.Template,
    promptregistry.WithCommitMessage(modified.CommitMessage),
)
fmt.Printf("Created version %d\n", newVersion.Version)

Debug Logging

handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
    Level: slog.LevelDebug,
})

client, err := mlflow.NewClient(
    mlflow.WithLogger(handler),
)

MCP Registry

The MCP Registry manages Model Context Protocol servers: a centralized catalog for registering, versioning, and sharing MCP servers across a team. Unlike the Prompt Registry and Tracking APIs, the MCP Registry REST endpoints are JSON based rather than protobuf based.

Register a Server and a Version

import "github.com/opendatahub-io/mlflow-go/mlflow/mcpregistry"

server, err := client.MCPRegistry().CreateMCPServer(ctx, "com.example/my-server",
    mcpregistry.WithServerDescription("Internal tools server"),
)

serverJSON := map[string]any{
    "name":    "com.example/my-server",
    "version": "1.0.0",
    "packages": []map[string]any{
        {"registryType": "npm", "identifier": "@example/my-mcp-server", "version": "1.0.0"},
    },
}

version, err := client.MCPRegistry().CreateMCPServerVersion(ctx, server.Name, serverJSON,
    mcpregistry.WithVersionStatus(mcpregistry.MCPServerVersionStatusActive),
    mcpregistry.WithVersionConnectOptions(map[string]mcpregistry.ConnectOptionSettings{
        "npm": {Hidden: true},
    }),
)
fmt.Printf("Created %s v%s with %d tools\n", version.Name, version.Version, len(version.Tools))

Update and Delete Servers and Versions

// Partial update: only fields configured via options are changed
server, err = client.MCPRegistry().UpdateMCPServer(ctx, server.Name,
    mcpregistry.WithUpdatedServerDisplayName("My Server"),
)

version, err = client.MCPRegistry().UpdateMCPServerVersion(ctx, server.Name, version.Version,
    mcpregistry.WithUpdatedVersionStatus(mcpregistry.MCPServerVersionStatusDeprecated),
)

err = client.MCPRegistry().DeleteMCPServerVersion(ctx, server.Name, version.Version)
err = client.MCPRegistry().DeleteMCPServer(ctx, server.Name)

Aliases

err := client.MCPRegistry().SetMCPServerAlias(ctx, "com.example/my-server", "production", "1.0.0")

version, err := client.MCPRegistry().GetMCPServerVersionByAlias(ctx, "com.example/my-server", "production")

err = client.MCPRegistry().DeleteMCPServerAlias(ctx, "com.example/my-server", "production")

Search Servers and Versions

servers, err := client.MCPRegistry().SearchMCPServers(ctx,
    mcpregistry.WithServersFilter("name LIKE 'com.example%'"),
    mcpregistry.WithServersMaxResults(20),
)

versions, err := client.MCPRegistry().SearchMCPServerVersions(ctx, "com.example/my-server")

Create and Manage Access Endpoints

// Bind a reachable URL to the server's "production" alias
endpoint, err := client.MCPRegistry().CreateMCPAccessEndpoint(ctx,
    "com.example/my-server", "https://mcp.example.com/my-server",
    mcpregistry.WithAccessEndpointServerAlias("production"),
)

// Search endpoints across all servers, or scope to a single server
endpoints, err := client.MCPRegistry().SearchMCPAccessEndpoints(ctx,
    mcpregistry.WithAccessEndpointsServerName("com.example/my-server"),
)

// Update the endpoint's URL or pin it to a different version/alias
endpoint, err = client.MCPRegistry().UpdateMCPAccessEndpoint(ctx,
    "com.example/my-server", endpoint.ID,
    mcpregistry.WithUpdatedEndpointServerAlias("staging"),
)

err = client.MCPRegistry().DeleteMCPAccessEndpoint(ctx, "com.example/my-server", endpoint.ID)

Tags

err := client.MCPRegistry().SetMCPServerTag(ctx, "com.example/my-server", "team", "platform")
err = client.MCPRegistry().DeleteMCPServerTag(ctx, "com.example/my-server", "team")

// Tags can also be set on a specific version
err = client.MCPRegistry().SetMCPServerVersionTag(ctx, "com.example/my-server", "1.0.0", "stage", "prod")
err = client.MCPRegistry().DeleteMCPServerVersionTag(ctx, "com.example/my-server", "1.0.0", "stage")

Error Handling

The SDK provides type-safe error checking:

prompt, err := client.PromptRegistry().LoadPrompt(ctx, "my-prompt")
if err != nil {
    switch {
    case mlflow.IsNotFound(err):
        fmt.Println("Prompt does not exist")
    case mlflow.IsUnauthorized(err):
        fmt.Println("Invalid token")
    case mlflow.IsPermissionDenied(err):
        fmt.Println("Access denied")
    case mlflow.IsAliasConflict(err):
        fmt.Println("Cannot delete: aliases point to this version (Databricks only)")
    default:
        var apiErr *mlflow.APIError
        if errors.As(err, &apiErr) {
            fmt.Printf("API error: %s (code: %s)\n",
                apiErr.Message, apiErr.Code)
        }
    }
    return
}

Feature Comparison with Python SDK

Experiment Tracking

Feature Status
Create/get/update/delete experiments ✅ Supported
Create/get/update/delete runs ✅ Supported
Log metrics (single + batch) ✅ Supported
Log params (single + batch) ✅ Supported
Set/delete tags (single + batch) ✅ Supported
Search experiments and runs ✅ Supported
Typed run status and view type ✅ Supported
Experiment kinds (UI classification) ✅ Supported
Set experiment tags ✅ Supported
Restore experiments/runs ❌ Not yet
Metric history ❌ Not yet
Artifact management ✅ Supported (list, upload, download)

Prompt Registry

Feature Status
Load/register prompts ✅ Supported
Text and chat prompts ✅ Supported
Variable substitution ({{var}}) ✅ Supported
Model configuration ✅ Supported
Version and alias management ✅ Supported
List/search with filters ✅ Supported
Tags on registration ✅ Supported
Delete prompts and versions ✅ Supported
Delete tags ✅ Supported
Custom headers (WithHeaders) ✅ Supported
Workspace isolation (midstream) ✅ Supported
Set/update tags after creation ❌ Not yet
Update model config after creation ❌ Not yet
Jinja2 templates (conditionals, loops) ❌ Not yet
Response format specification ❌ Not yet
Cache TTL configuration ❌ Not yet

MCP Registry

Feature Status
Register/get/update/delete/search servers ✅ Supported
Create/get/update/delete/search server versions ✅ Supported
Per-connection-mode display settings (connect_options) ✅ Supported
Create/get/update/search/delete access endpoints ✅ Supported
Set/delete tags on servers and versions ✅ Supported
Alias management (set/get/delete) ✅ Supported

Development

Prerequisites

  • Go 1.23+
  • MLflow server (for integration tests)

Commands

# Run unit tests
make test/unit

# Run linter
make lint

# Run all checks (lint, vet, tests)
make check

# Start local MLflow server (requires uv)
make dev/up

# Start MLflow from opendatahub-io/mlflow fork (with workspaces enabled)
make dev/up-midstream

# Create test workspaces (team-bella and team-dora)
make dev/seed-workspaces

# Seed sample prompts (featuring Bella and Dora!)
make dev/seed

# Run the sample app
make run-sample

# Run workspace isolation demo (requires midstream server)
make dev/up-midstream   # in one terminal
make dev/seed-workspaces
make run-sample-workspaces

# Run sample app against a remote MLflow instance
cp .env.local.example .env.local   # fill in credentials
make run-sample-remote

# Run integration tests
make test/integration

# Run integration tests against midstream
make test/integration-ci-midstream

# Stop local MLflow server
make dev/down

# Reset MLflow (nuke data)
make dev/reset

Project Structure

mlflow-go/
├── mlflow/                     # Public SDK package
│   ├── client.go               # Root client with domain accessors
│   ├── options.go              # Client-level options
│   ├── errors.go               # Error types and helpers
│   ├── tracking/               # Experiment Tracking sub-client
│   │   ├── client.go           # Tracking API methods
│   │   ├── types.go            # Experiment, Run, Metric, Param types
│   │   └── options.go          # Domain-specific options
│   ├── artifacts/              # Artifacts sub-client
│   │   ├── client.go           # ListArtifacts, LogArtifact, DownloadArtifact, UploadArtifact, DownloadArtifactByPath
│   │   ├── types.go            # FileInfo, ListArtifactsResult types
│   │   └── options.go          # Domain-specific options
│   ├── promptregistry/         # Prompt Registry sub-client
│   │   ├── client.go           # PromptRegistry API methods
│   │   ├── prompt.go           # Prompt, PromptInfo types
│   │   └── options.go          # Domain-specific options
│   └── mcpregistry/            # MCP Registry sub-client
│       ├── client.go           # MCPRegistry API methods
│       ├── server.go           # MCPServer, MCPServerVersion, MCPAccessEndpoint types
│       └── options.go          # Domain-specific options
├── internal/                   # Internal packages
│   ├── conv/                   # Shared type-conversion helpers
│   ├── errors/                 # APIError implementation
│   ├── artifact/               # Artifact URI resolution and store logic
│   └── transport/              # HTTP client
├── sample-app/                 # Demo application
└── specs/                      # Design documentation

License

Apache 2.0 - see LICENSE

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages