|
| 1 | +# Profiles Feature Design |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Add named profile support to the langsmith CLI, allowing users to store and switch between multiple LangSmith configurations (API endpoint, credentials, workspace). Profiles are stored in a TOML config file and selected via CLI flag, environment variable, or a persistent `current_profile` setting. |
| 6 | + |
| 7 | +## Config File |
| 8 | + |
| 9 | +- **Location:** `~/.langsmith/config.toml` |
| 10 | +- **Format:** TOML with a top-level `current_profile` key and one `[section]` per profile |
| 11 | + |
| 12 | +```toml |
| 13 | +current_profile = "default" |
| 14 | + |
| 15 | +[default] |
| 16 | +api_url = "https://api.smith.langchain.com" |
| 17 | +api_key = "lsv2_pt_abc123..." |
| 18 | +workspace_id = "optional-uuid" |
| 19 | + |
| 20 | +[staging] |
| 21 | +api_url = "https://staging.api.smith.langchain.com" |
| 22 | +api_key = "lsv2_pt_def456..." |
| 23 | +``` |
| 24 | + |
| 25 | +### Profile Fields |
| 26 | + |
| 27 | +| Field | Required | Default | |
| 28 | +|----------------|----------|----------------------------------------| |
| 29 | +| `api_key` | yes | — | |
| 30 | +| `api_url` | no | `https://api.smith.langchain.com` | |
| 31 | +| `workspace_id` | no | — | |
| 32 | + |
| 33 | +## Resolution Priority |
| 34 | + |
| 35 | +Credential and endpoint resolution follows AWS-style precedence (highest wins): |
| 36 | + |
| 37 | +1. `--api-key` / `--api-url` CLI flags |
| 38 | +2. `LANGSMITH_API_KEY` / `LANGSMITH_ENDPOINT` / `LANGSMITH_WORKSPACE_ID` env vars |
| 39 | +3. Named profile selected via `--profile` flag or `LANGSMITH_PROFILE` env var |
| 40 | +4. `current_profile` value from config file |
| 41 | +5. `[default]` profile in config file |
| 42 | +6. Hardcoded default URL (`https://api.smith.langchain.com`) |
| 43 | + |
| 44 | +If no config file exists and no env vars or flags are set, the CLI behaves exactly as it does today (requires `LANGSMITH_API_KEY`). |
| 45 | + |
| 46 | +## Subcommands |
| 47 | + |
| 48 | +All under `langsmith profile`: |
| 49 | + |
| 50 | +### `langsmith profile list` |
| 51 | + |
| 52 | +Lists all profiles. Marks the active profile with `*`. |
| 53 | + |
| 54 | +- JSON output: array of objects with `name`, `api_url`, `active` fields (key never shown) |
| 55 | +- Pretty output: table with name, URL, active marker |
| 56 | + |
| 57 | +### `langsmith profile show <name>` |
| 58 | + |
| 59 | +Shows a single profile's configuration. The API key is masked (e.g., `lsv2_pt_...c123`). |
| 60 | + |
| 61 | +- JSON output: object with `name`, `api_url`, `api_key` (masked), `workspace_id` |
| 62 | +- Pretty output: key-value display |
| 63 | + |
| 64 | +### `langsmith profile create <name>` |
| 65 | + |
| 66 | +Creates a new profile. Accepts flags for scripting; prompts interactively for missing required fields. |
| 67 | + |
| 68 | +**Flags:** |
| 69 | +- `--api-key` (required, or prompted) |
| 70 | +- `--api-url` (optional, defaults to `https://api.smith.langchain.com`) |
| 71 | +- `--workspace-id` (optional) |
| 72 | + |
| 73 | +If `~/.langsmith/config.toml` does not exist, it is created. If this is the first profile and no `current_profile` is set, it becomes the current profile automatically. |
| 74 | + |
| 75 | +Errors if a profile with that name already exists. |
| 76 | + |
| 77 | +### `langsmith profile delete <name>` |
| 78 | + |
| 79 | +Removes a profile from the config file. |
| 80 | + |
| 81 | +Errors if the profile does not exist. If the deleted profile was the `current_profile`, clears `current_profile` (the user must `profile use` another one or rely on env vars). |
| 82 | + |
| 83 | +### `langsmith profile use <name>` |
| 84 | + |
| 85 | +Sets `current_profile` in the config file to the given profile name. |
| 86 | + |
| 87 | +Errors if the profile does not exist. |
| 88 | + |
| 89 | +## Root Command Changes |
| 90 | + |
| 91 | +- New `--profile` persistent flag on the root command |
| 92 | +- `GetAPIKey()`, `GetAPIURL()` updated to consult profile config as a fallback after checking flags and env vars |
| 93 | +- New helper to resolve workspace ID from profile |
| 94 | + |
| 95 | +## New Package: `internal/config` |
| 96 | + |
| 97 | +Handles reading, writing, and querying the TOML config file. |
| 98 | + |
| 99 | +### Key types and functions: |
| 100 | + |
| 101 | +```go |
| 102 | +// Profile represents a single named profile. |
| 103 | +type Profile struct { |
| 104 | + APIKey string `toml:"api_key"` |
| 105 | + APIURL string `toml:"api_url,omitempty"` |
| 106 | + WorkspaceID string `toml:"workspace_id,omitempty"` |
| 107 | +} |
| 108 | + |
| 109 | +// Config represents the full config file. |
| 110 | +type Config struct { |
| 111 | + CurrentProfile string `toml:"current_profile,omitempty"` |
| 112 | + Profiles map[string]Profile // each TOML section is a profile |
| 113 | +} |
| 114 | + |
| 115 | +func Load() (*Config, error) // reads ~/.langsmith/config.toml, returns empty Config if missing |
| 116 | +func (c *Config) Save() error // writes back to ~/.langsmith/config.toml |
| 117 | +func (c *Config) ActiveProfileName(flagProfile, envProfile string) string |
| 118 | +func (c *Config) ResolveProfile(flagProfile, envProfile string) *Profile |
| 119 | +``` |
| 120 | + |
| 121 | +## Behavioral Notes |
| 122 | + |
| 123 | +- The config file is only created on first `profile create`, never eagerly. |
| 124 | +- All existing behavior is preserved when no config file exists. |
| 125 | +- `profile delete` of the active profile clears `current_profile` rather than picking a new one automatically. |
| 126 | +- API key masking shows first 8 and last 4 characters (e.g., `lsv2_pt_...c123`). |
| 127 | + |
| 128 | +## Out of Scope |
| 129 | + |
| 130 | +- `langsmith login` (OAuth/browser-based auth flow) — future work |
| 131 | +- Config file locking for concurrent access — single-user CLI |
| 132 | +- Profile import/export |
0 commit comments