Skip to content

Add support for ${MODEL_ID} macro#226

Merged
mostlygeek merged 1 commit intomostlygeek:mainfrom
brettp:model_id_macro
Sep 2, 2025
Merged

Add support for ${MODEL_ID} macro#226
mostlygeek merged 1 commit intomostlygeek:mainfrom
brettp:model_id_macro

Conversation

@brettp
Copy link
Contributor

@brettp brettp commented Aug 7, 2025

This adds ${MODEL_ID} support in commands and user-defined macros to simplify config. For example, this common config for docker:

macros:
  "docker-llama": docker run -v /models:/models -p ${PORT}:8080

models:
  gpt-oss-20b-GGUF:
    cmd: |
      ${docker-llama} 
      --name gpt-oss-20b-GGUF
      ghcr.io/ggml-org/llama.cpp:server-cuda
    cmdStop: docker stop gpt-oss-20b-GGUF

  Qwen3-Coder-30B-A3B-Instruct-GGUF:
    cmd: ${docker-llama} --name Qwen3-Coder-30B-A3B-Instruct-GGUF ghcr.io/ggml-org/llama.cpp:server-cuda
    cmdStop: docker stop Qwen3-Coder-30B-A3B-Instruct-GGUF

...can now be simplified to be more readable and less prone to copy and paste errors:

macros:
  "docker-llama": |
    docker run -v /models:/models 
    -p ${PORT}:8080 
    --name ${MODEL_ID} 
    ghcr.io/ggml-org/llama.cpp:server-cuda
  "docker-stop": |
    docker stop ${MODEL_ID}

models:
  gpt-oss-20b-GGUF:
    cmd: ${docker-llama} -hf unsloth/gpt-oss-20b-GGUF:F16
    useModelName: unsloth/gpt-oss-20b-GGUF:F16
    cmdStop: ${docker-stop}

  Qwen3-Coder-30B-A3B-Instruct-GGUF:
    cmd: ${docker-llama} -hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL
    useModelName: unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL
    cmdStop: ${docker-stop}

Summary by CodeRabbit

  • Documentation

    • Clarified ${MODEL_ID} usage in macros and examples (updated docker-llama example and notes).
  • New Features

    • ${MODEL_ID} now expands in per-model commands (cmd/cmdStop) so container commands are tied to each model.
  • Bug Fixes

    • MODEL_ID reserved to avoid conflicts with user-defined macros.
  • Tests

    • Added tests verifying ${MODEL_ID} and ${PORT} expand correctly and produce expected per-model commands.

@coderabbitai
Copy link

coderabbitai bot commented Aug 7, 2025

Walkthrough

Adds a reserved ${MODEL_ID} macro, expands it into each model's Cmd and CmdStop during per-model processing, updates the example config to use ${MODEL_ID} for container names, and adds a test verifying ${MODEL_ID} and ${PORT} expansions across models.

Changes

Cohort / File(s) Change Summary
Example config
config.example.yaml
Note added that ${MODEL_ID} is available in macros; example docker-llama updated to use ${MODEL_ID} in cmd and cmdStop.
Macro expansion logic
proxy/config.go
Added MODEL_ID to reserved macro names; during per-model processing replace ${MODEL_ID} with the model's identifier in Cmd and CmdStop before unknown-macro validation. ${PORT} handling unchanged.
Tests
proxy/config_test.go
Added TestConfig_MacroModelId to assert ${MODEL_ID} and ${PORT} expand correctly for multiple models (including IDs with / and :) and that macro definitions remain unexpanded in the macro map.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant ConfigLoader
    participant MacroExpander
    participant ModelProcessor

    User->>ConfigLoader: Load config with macros + models
    ConfigLoader->>MacroExpander: Expand global/user macros
    MacroExpander-->>ConfigLoader: Global macros expanded
    ConfigLoader->>ModelProcessor: For each model, start per-model processing
    Note right of ModelProcessor: New step — substitute `${MODEL_ID}` in Cmd & CmdStop
    ModelProcessor->>ModelProcessor: Replace `${MODEL_ID}` → modelId
    ModelProcessor->>ModelProcessor: Validate remaining macros (e.g., `${PORT}`)
    ModelProcessor-->>ConfigLoader: Return expanded per-model fields
    ConfigLoader-->>User: Provide sanitized, per-model config
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement, configuration

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45ea792 and cfec2b3.

📒 Files selected for processing (3)
  • config.example.yaml (3 hunks)
  • proxy/config.go (3 hunks)
  • proxy/config_test.go (1 hunks)
🔇 Additional comments (6)
proxy/config.go (1)

245-245: LGTM: MODEL_ID correctly added to reserved macros.

Adding "MODEL_ID" to the reserved macro list prevents users from defining their own MODEL_ID macro, which is appropriate since this is now a built-in system macro.

config.example.yaml (4)

44-44: Excellent documentation addition.

This line clearly explains that the model ID is available as the ${MODEL_ID} macro, which helps users understand this new feature.


134-134: Clear example documentation.

The updated comment appropriately describes the combination of cmd, cmdStop, and ${MODEL_ID} usage.


138-138: Perfect example of MODEL_ID macro usage.

Replacing the hardcoded container name with ${MODEL_ID} demonstrates the key benefit of this feature - eliminating redundancy and copy-paste errors.


150-150: Consistent macro usage in cmdStop.

Using ${MODEL_ID} in the cmdStop command maintains consistency and shows how the macro works across different command fields.

proxy/config_test.go (1)

444-483: Comprehensive test coverage for MODEL_ID macro functionality.

This test thoroughly validates the MODEL_ID macro implementation:

✅ Tests basic MODEL_ID expansion in model commands
✅ Tests MODEL_ID expansion within user-defined macros
✅ Verifies PORT macro continues to work with incremental assignment
✅ Tests complex model IDs with special characters ("author/model:F16")
✅ Confirms that macro definitions in config.Macros remain unexpanded
✅ Validates both cmd and cmdStop field expansion

The test cases cover the key scenarios and edge cases effectively.

@brettp brettp marked this pull request as draft August 7, 2025 04:58
@brettp
Copy link
Contributor Author

brettp commented Aug 7, 2025

The config above fails because of #229. Even without the model ID matching the HF repo/model:quant, the config is simplified because it doesn't require splitting the docker command for the --name arg.

I'm happy to add tests for #229 and fix the config example if this is of interest.

@bjodah
Copy link

bjodah commented Aug 17, 2025

I would also be interested in this functionality, I often find myself repeating the model-id in e.g. the --log-file argument to llama-server. Being able to write e.g.:

foo: |
   llama-server \
       --more-flags ... \
       --log-file /var/logs/llamacpp/${MODEL_ID}.log

helps me to better adhere to the "DRY"-principle in my config.

@brettp brettp marked this pull request as ready for review August 31, 2025 22:34
@brettp
Copy link
Contributor Author

brettp commented Aug 31, 2025

This is ready for review.

I've rebased to main and updated the examples here. Thought not quite as DRY as intended, still an improvement!

proxy/config.go Outdated
modelConfig.CmdStop = strings.ReplaceAll(modelConfig.CmdStop, "${MODEL_ID}", modelId)
modelConfig.Proxy = strings.ReplaceAll(modelConfig.Proxy, "${MODEL_ID}", modelId)
modelConfig.CheckEndpoint = strings.ReplaceAll(modelConfig.CheckEndpoint, "${MODEL_ID}", modelId)
modelConfig.Filters.StripParams = strings.ReplaceAll(modelConfig.Filters.StripParams, "${MODEL_ID}", modelId)
Copy link
Owner

Choose a reason for hiding this comment

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

I don’t think we need to support model_id in stripParams, checkEndpoint and proxy because it’ll have limited usefulness. Unless there’s a clear use case I would bias towards just cmd and cmdStop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, nothing immediate that wouldn't be better solved elsewhere.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
config.example.yaml (4)

52-53: Clarify ${MODEL_ID} scope to match implementation (cmd/cmdStop only).

Implementation expands ${MODEL_ID} only in cmd and cmdStop. Reword to avoid implying support in proxy/checkEndpoint/filters.

-# - the model's ID is available in the ${MODEL_ID} macro, also available in macros defined above.
+# - the model's ID is available via ${MODEL_ID} for use in cmd and cmdStop
+# - macros defined above may include ${MODEL_ID} if they ultimately expand into those fields

152-154: Fix grammar: “runtimes” and remove duplicate “a”.

-  # container run times like Docker and Podman can be used reliably with a
-  # a combination of cmd, cmdStop, and ${MODEL_ID}
+  # container runtimes like Docker and Podman can be used reliably with
+  # a combination of cmd, cmdStop, and ${MODEL_ID}

156-160: Warn that ${MODEL_ID} may be an invalid Docker container name.

IDs containing “/” or “:” won’t be valid for --name; the example could mislead. Add a brief note.

     cmd: |
+      # NOTE: If you use ${MODEL_ID} for --name, ensure your model IDs are valid Docker container
+      # names (letters, digits, '_', '.', '-'). IDs with '/' or ':' will fail.
       docker run --name ${MODEL_ID}
       --init --rm -p ${PORT}:8080 -v /mnt/nvme/models:/models
       ghcr.io/ggml-org/llama.cpp:server
       --model '/models/Qwen2.5-Coder-0.5B-Instruct-Q4_K_M.gguf'

171-171: Mirror the Docker-name caveat for cmdStop.

-    cmdStop: docker stop ${MODEL_ID}
+    # NOTE: This will fail if ${MODEL_ID} is not a valid Docker container name.
+    cmdStop: docker stop ${MODEL_ID}
proxy/config.go (1)

236-243: Keep the reserved-macro comment in sync (include MODEL_ID).

  /* check macro constraint rules:
 
   - name must fit the regex ^[a-zA-Z0-9_-]+$
   - names must be less than 64 characters (no reason, just cause)
-  - name can not be any reserved macros: PORT
+  - name can not be any reserved macros: PORT, MODEL_ID
   - macro values must be less than 1024 characters
  */
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1030c01 and 2e905de.

📒 Files selected for processing (3)
  • config.example.yaml (3 hunks)
  • proxy/config.go (2 hunks)
  • proxy/config_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • proxy/config_test.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: brettp
PR: mostlygeek/llama-swap#226
File: proxy/config.go:305-313
Timestamp: 2025-08-07T04:50:07.200Z
Learning: In the llama-swap project, model IDs are treated as trusted input from YAML configuration files. Additional validation for MODEL_ID macro expansion is not needed because if someone can control model IDs in the config, they can already inject malicious content in other fields like cmd. The configuration file is the trusted source.
📚 Learning: 2025-08-07T04:50:07.200Z
Learnt from: brettp
PR: mostlygeek/llama-swap#226
File: proxy/config.go:305-313
Timestamp: 2025-08-07T04:50:07.200Z
Learning: In the llama-swap project, model IDs are treated as trusted input from YAML configuration files. Additional validation for MODEL_ID macro expansion is not needed because if someone can control model IDs in the config, they can already inject malicious content in other fields like cmd. The configuration file is the trusted source.

Applied to files:

  • config.example.yaml
  • proxy/config.go
🔇 Additional comments (1)
proxy/config.go (1)

254-258: LGTM: reserving MODEL_ID prevents user-defined collisions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2e905de and 3f7827f.

📒 Files selected for processing (3)
  • config.example.yaml (3 hunks)
  • proxy/config.go (3 hunks)
  • proxy/config_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • proxy/config_test.go
  • proxy/config.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: brettp
PR: mostlygeek/llama-swap#226
File: proxy/config.go:305-313
Timestamp: 2025-08-07T04:50:07.200Z
Learning: In the llama-swap project, model IDs are treated as trusted input from YAML configuration files. Additional validation for MODEL_ID macro expansion is not needed because if someone can control model IDs in the config, they can already inject malicious content in other fields like cmd. The configuration file is the trusted source.
📚 Learning: 2025-08-07T04:50:07.200Z
Learnt from: brettp
PR: mostlygeek/llama-swap#226
File: proxy/config.go:305-313
Timestamp: 2025-08-07T04:50:07.200Z
Learning: In the llama-swap project, model IDs are treated as trusted input from YAML configuration files. Additional validation for MODEL_ID macro expansion is not needed because if someone can control model IDs in the config, they can already inject malicious content in other fields like cmd. The configuration file is the trusted source.

Applied to files:

  • config.example.yaml

# - required
# - each key is the model's ID, used in API requests
# - model settings have default values that are used if they are not defined here
# - the model's ID is available in the ${MODEL_ID} macro, also available in macros defined above
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clarify where ${MODEL_ID} expands (cmd/cmdStop only).

As implemented, ${MODEL_ID} is substituted in model.cmd and model.cmdStop (not in proxy or checkEndpoint). Update the comment to avoid implying broader scope.

Apply this diff:

-# - the model's ID is available in the ${MODEL_ID} macro, also available in macros defined above
+# - the model's ID is available as ${MODEL_ID} in model.cmd and model.cmdStop
+# - macros that are referenced by cmd/cmdStop may also include ${MODEL_ID}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# - the model's ID is available in the ${MODEL_ID} macro, also available in macros defined above
# - the model's ID is available as ${MODEL_ID} in model.cmd and model.cmdStop
# - macros that are referenced by cmd/cmdStop may also include ${MODEL_ID}
🤖 Prompt for AI Agents
In config.example.yaml around line 52, the comment incorrectly implies
${MODEL_ID} is available broadly; clarify that ${MODEL_ID} is only substituted
into model.cmd and model.cmdStop. Update the comment text to state explicitly
that the ${MODEL_ID} macro is available only for model.cmd and model.cmdStop
(and not in proxy or checkEndpoint), so users won’t assume it expands elsewhere.

Comment on lines +152 to 154
# container runtimes like Docker and Podman can be used reliably with
# a combination of cmd, cmdStop, and ${MODEL_ID}
"docker-llama":
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid suggesting ${MODEL_ID} for container names; many model IDs are not Docker/Podman-safe.

Model IDs can contain '/', ':' (e.g., author/model:variant), which are invalid in container names. Recommend ${PORT} (numeric, unique per model using it) for names and stopping commands. This is a documentation/runtime correctness fix; MODEL_ID remains trusted input per project learnings.

Apply this diff:

-  # container runtimes like Docker and Podman can be used reliably with
-  # a combination of cmd, cmdStop, and ${MODEL_ID}
+  # container runtimes like Docker and Podman can be used with cmd/cmdStop.
+  # NOTE: prefer ${PORT} for container names; ${MODEL_ID} may contain '/' or ':' and won't be a valid container name.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# container runtimes like Docker and Podman can be used reliably with
# a combination of cmd, cmdStop, and ${MODEL_ID}
"docker-llama":
# container runtimes like Docker and Podman can be used with cmd/cmdStop.
# NOTE: prefer ${PORT} for container names; ${MODEL_ID} may contain '/' or ':' and won't be a valid container name.
"docker-llama":
🤖 Prompt for AI Agents
In config.example.yaml around lines 152 to 154, the docs currently suggest using
${MODEL_ID} in container names/stop commands which is unsafe because model IDs
may contain characters invalid for Docker/Podman; update the text and any
examples to recommend using ${PORT} (a numeric, unique per-model identifier) for
container names and stop commands instead, ensure guidance notes "${PORT} must
be unique per model instance and numeric", and adjust any example command
snippets to use ${PORT} in place of ${MODEL_ID}.

proxy: "http://127.0.0.1:${PORT}"
cmd: |
docker run --name dockertest
docker run --name ${MODEL_ID}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use a PORT-based container name to ensure validity across all model IDs.

Prevents failures when MODEL_ID contains invalid characters for container names.

Apply this diff:

-      docker run --name ${MODEL_ID}
+      docker run --name llama-${PORT}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
docker run --name ${MODEL_ID}
docker run --name llama-${PORT}
🤖 Prompt for AI Agents
In config.example.yaml around line 157, the docker run command uses --name
${MODEL_ID} which can fail if MODEL_ID contains invalid container-name
characters; change the container name to use a PORT-based, always-valid
identifier (for example --name model_${PORT} or --name model-${PORT}) and update
any related references/env docs to use the PORT-based name so container naming
is deterministic and valid across all model IDs.

# - on Windows, calls taskkill to stop the process
# - processes have 5 seconds to shutdown until forceful termination is attempted
cmdStop: docker stop dockertest
cmdStop: docker stop ${MODEL_ID}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Align cmdStop with the PORT-based container name.

Keeps start/stop symmetric and robust to arbitrary model IDs.

Apply this diff:

-    cmdStop: docker stop ${MODEL_ID}
+    cmdStop: docker stop llama-${PORT}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cmdStop: docker stop ${MODEL_ID}
cmdStop: docker stop llama-${PORT}
🤖 Prompt for AI Agents
In config.example.yaml at line 171, cmdStop currently uses docker stop
${MODEL_ID}; change it to stop the container using the same PORT-based container
name pattern used by cmdStart so start/stop are symmetric—replace ${MODEL_ID}
with the exact PORT-based container name expression used in cmdStart (for
example ${PORT}_${MODEL_ID} or whatever pattern is used elsewhere in this file).

@mostlygeek mostlygeek merged commit 97b17fc into mostlygeek:main Sep 2, 2025
3 checks passed
@mostlygeek
Copy link
Owner

@brettp looks good! Thank you for the contribution.

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.

3 participants