Skip to content

feat: add named profile support#52

Open
Mukil Loganathan (langchain-infra) wants to merge 5 commits intofeature/api-commandfrom
feature/profiles
Open

feat: add named profile support#52
Mukil Loganathan (langchain-infra) wants to merge 5 commits intofeature/api-commandfrom
feature/profiles

Conversation

@langchain-infra
Copy link
Copy Markdown
Contributor

@langchain-infra Mukil Loganathan (langchain-infra) commented Apr 3, 2026

Summary

  • Adds langsmith profile subcommand group (create, list, show, delete, use) for managing named CLI profiles
  • Profiles store API key, endpoint URL, and workspace ID in ~/.langsmith/config.toml (TOML format)
  • New --profile flag and LANGSMITH_PROFILE env var for selecting active profile
  • Resolution priority: CLI flags > env vars > named profile > default profile > hardcoded defaults
  • client.New now accepts workspace ID as a parameter instead of reading env directly

Usage

# Create a profile
langsmith profile create prod --api-key lsv2_pt_... --api-url https://api.smith.langchain.com

# Create a second profile
langsmith profile create staging --api-key lsv2_pt_... --api-url https://staging.example.com

# List profiles (* marks active)
langsmith profile list
# [{"name":"prod","api_url":"https://api.smith.langchain.com","active":true},
#  {"name":"staging","api_url":"https://staging.example.com","active":false}]

# Switch active profile
langsmith profile use staging

# Show profile config (API key is masked)
langsmith profile show staging
# {"name":"staging","api_key":"lsv2_pt_...g456","api_url":"https://staging.example.com"}

# Use a profile for a single command
langsmith project list --profile prod

# Or via environment variable
LANGSMITH_PROFILE=staging langsmith project list

# Delete a profile
langsmith profile delete staging

Config file format (~/.langsmith/config.toml)

current_profile = "prod"

[prod]
api_key = "lsv2_pt_abc123..."
api_url = "https://api.smith.langchain.com"
workspace_id = "ws-optional"

[staging]
api_key = "lsv2_pt_def456..."
api_url = "https://staging.example.com"

Test plan

  • Unit tests for config package (load, save, round-trip, permissions, resolution precedence, masking)
  • Unit tests for all 5 profile subcommands (happy path + error cases)
  • Unit tests for profile fallback in GetAPIKey/GetAPIURL/GetWorkspaceID
  • E2E smoke test: create → list → show → use → delete flow
  • All existing tests pass (go test ./...)
  • Lint clean (golangci-lint run)

…uthenticated requests

Adds a new `langsmith api` command inspired by `gh api`, `vercel api`, and `az rest`.
Three modes: browse endpoints via OpenAPI spec, inspect endpoint details, and make
authenticated HTTP requests with automatic header injection.

New commands:
- `langsmith api ls` — list all API endpoints with --tag and --search filters
- `langsmith api info METHOD path` — show endpoint params, request body, response schema
- `langsmith api METHOD path` — make authenticated requests with --body, -H, --include

OpenAPI spec is fetched from /openapi.json and cached locally with 24h TTL.
Export the shared helper functions from the cmd package so they can be
used by sub-packages. The api sub-package still uses cobra flag
inheritance (different mechanism) due to circular import constraints,
but the rest of the codebase now uses the exported names consistently.
When a full URL to a different host is passed (e.g.,
https://other.host/foo), use an empty-base client so RawDo doesn't
prepend the default apiURL. Fixes malformed URL bug reported in PR
review.
devin-ai-integration[bot]

This comment was marked as resolved.

Add profile management to the CLI, allowing users to store and switch
between multiple LangSmith configurations (API key, endpoint, workspace)
via ~/.langsmith/config.toml.

New subcommands:
  langsmith profile create/list/show/delete/use

New flags:
  --profile, LANGSMITH_PROFILE env var

Resolution priority: flags > env vars > named profile > default profile.

Includes internal/config package for TOML config read/write and
comprehensive test coverage across config, profile commands, and
resolution logic.
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines 241 to 244
func TestRawRequest_NoWorkspaceHeaderWhenUnset(t *testing.T) {
t.Setenv("LANGSMITH_WORKSPACE_ID", "")

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("x-tenant-id"); got != "" {
t.Errorf("expected empty x-tenant-id, got %q", got)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Test TestRawRequest_NoWorkspaceHeaderWhenUnset is environment-dependent after removing t.Setenv

The removal of t.Setenv("LANGSMITH_WORKSPACE_ID", "") from TestRawRequest_NoWorkspaceHeaderWhenUnset makes the test fail if the developer or CI environment has LANGSMITH_WORKSPACE_ID set to a non-empty value. The client.New() function (internal/client/client.go:50) and rawRequest() (internal/client/client.go:162) both read this env var and will set the x-tenant-id header, causing the assertion at line 243 to fail. The previous code explicitly cleared this variable, ensuring the test was self-contained regardless of the environment.

(Refers to lines 241-253)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant