-
Notifications
You must be signed in to change notification settings - Fork 86
LCORE-2072: Add SkillsConfiguration model to config file #1736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12081,6 +12081,18 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/RerankerConfiguration", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "title": "Reranker configuration", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Configuration for neural reranking of RAG chunks using cross-encoder." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "skills": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "anyOf": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/SkillsConfiguration" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "null" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "title": "Agent skills", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Agent skills configuration. Specifies paths to skill directories." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "additionalProperties": false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -19381,6 +19393,23 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "SkillsConfiguration": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "properties": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "paths": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "items": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "format": "path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "array", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "title": "Skill paths", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Paths to skill directories or directories containing skill subdirectories." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "additionalProperties": false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "title": "SkillsConfiguration", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Agent skills configuration.\n\nSpecifies paths to skill directories. Skill metadata (name, description)\nis read from SKILL.md frontmatter at startup.\n\nEach path can point to either:\n- A directory containing a SKILL.md file (single skill)\n- A directory containing subdirectories with SKILL.md files (multiple skills)\n\nPaths are validated at startup to ensure they exist and contain valid SKILL.md files." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19396
to
+19412
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add The ✨ Proposed fix "SkillsConfiguration": {
"properties": {
"paths": {
"items": {
"type": "string"
},
"type": "array",
"title": "Skill paths",
"description": "Paths to skill directories or directories containing skill subdirectories."
}
},
+ "required": ["paths"],
"additionalProperties": false,
"type": "object",
"title": "SkillsConfiguration",
"description": "Agent skills configuration.\n\nSpecifies paths to skill directories. Skill metadata (name, description)\nis read from SKILL.md frontmatter at startup.\n\nEach path can point to either:\n- A directory containing a SKILL.md file (single skill)\n- A directory containing subdirectories with SKILL.md files (multiple skills)\n\nPaths are validated at startup to ensure they exist and contain valid SKILL.md files."
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "SolrVectorSearchRequest": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "properties": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "mode": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,31 @@ | ||||||
| name: Lightspeed Core Service (LCS) with Skills | ||||||
| service: | ||||||
| host: localhost | ||||||
| port: 8080 | ||||||
| auth_enabled: false | ||||||
| workers: 1 | ||||||
| color_log: true | ||||||
| access_log: true | ||||||
| llama_stack: | ||||||
| use_as_library_client: true | ||||||
| library_client_config_path: run.yaml | ||||||
| user_data_collection: | ||||||
| feedback_enabled: true | ||||||
| feedback_storage: "/tmp/data/feedback" | ||||||
| transcripts_enabled: true | ||||||
| transcripts_storage: "/tmp/data/transcripts" | ||||||
| authentication: | ||||||
| module: "noop" | ||||||
| # Agent skills configuration | ||||||
| # Skills provide domain-specific instructions and reference materials | ||||||
| # that the LLM can load on demand when relevant to the current task | ||||||
| skills: | ||||||
| paths: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a reason that we need the paths? It would make sense if we had other data fields under skills but if paths is the only one then I think skills can just be a list. wdyt?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Based on further discussions: It's a little weird to look at now, but the current layout is the safest approach - I can't think of anything else that might be needed under the skills tab (eg, settings etc), but keeping it tabbe-ed future proofs it so keeping it as is |
||||||
| # Option A: Directory containing multiple skill subdirectories | ||||||
| # Each subdirectory must contain a SKILL.md file | ||||||
| - "/var/skills/" | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | 💤 Low value Minor: trailing slash inconsistency with test examples. Line 26 uses Consider removing the trailing slash to match the test patterns: - - "/var/skills/"
+ - "/var/skills"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| # Option B: Individual skill paths for fine-grained control | ||||||
| # - "/var/skills/openshift-troubleshooting/" | ||||||
| # - "/var/skills/code-review/" | ||||||
| # - "/opt/custom-skills/deployment-guide/" | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1944,6 +1944,26 @@ class AzureEntraIdConfiguration(ConfigurationBase): | |
| ) | ||
|
|
||
|
|
||
| class SkillsConfiguration(ConfigurationBase): | ||
| """Agent skills configuration. | ||
|
|
||
| Specifies paths to skill directories. Skill metadata (name, description) | ||
| is read from SKILL.md frontmatter at startup. | ||
|
|
||
| Each path can point to either: | ||
| - A directory containing a SKILL.md file (single skill) | ||
| - A directory containing subdirectories with SKILL.md files (multiple skills) | ||
|
|
||
| Paths are validated at startup to ensure they exist and contain valid SKILL.md files. | ||
| """ | ||
|
Comment on lines
+1957
to
+1958
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstring states validation that is not implemented yet. The text says paths are validated at startup, but this model currently has no validation logic. Please reword this to avoid implying enforced behavior in this commit. 🤖 Prompt for AI Agents |
||
|
|
||
| paths: list[Path] = Field( | ||
| default_factory=list, | ||
| title="Skill paths", | ||
| description="Paths to skill directories or directories containing skill subdirectories.", | ||
| ) | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prob. check if the directory is accessible. It might be too early to do it during config loading, but probably better from UX perspective (fail early...). WDYT?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm leaning towards keeping filesystem validation out of the Pedantic config layer - eg in containerized environments volumes may not be mounted yet when the config is parsed. It also makes unit testing harder since we have to create actual directories. Fwiw, |
||
|
|
||
| class Configuration(ConfigurationBase): | ||
| """Global service configuration.""" | ||
|
|
||
|
|
@@ -2110,6 +2130,12 @@ class Configuration(ConfigurationBase): | |
| description="Configuration for neural reranking of RAG chunks using cross-encoder.", | ||
| ) | ||
|
|
||
| skills: Optional[SkillsConfiguration] = Field( | ||
| default=None, | ||
| title="Agent skills", | ||
| description="Agent skills configuration. Specifies paths to skill directories.", | ||
| ) | ||
|
|
||
| @model_validator(mode="after") | ||
| def validate_mcp_auth_headers(self) -> Self: | ||
| """ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ | |
| QuotaLimiterConfiguration, | ||
| QuotaSchedulerConfiguration, | ||
| ServiceConfiguration, | ||
| SkillsConfiguration, | ||
| TLSConfiguration, | ||
| UserDataCollection, | ||
| ) | ||
|
|
@@ -235,6 +236,7 @@ def test_dump_configuration(tmp_path: Path) -> None: | |
| "enabled": False, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can enhance the config file used in dump tests, and then it will be possible to check for actual skills path processing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a |
||
| "model": "cross-encoder/ms-marco-MiniLM-L6-v2", | ||
| }, | ||
| "skills": None, | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -606,6 +608,7 @@ def test_dump_configuration_with_quota_limiters(tmp_path: Path) -> None: | |
| "enabled": False, | ||
| "model": "cross-encoder/ms-marco-MiniLM-L6-v2", | ||
| }, | ||
| "skills": None, | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -853,6 +856,7 @@ def test_dump_configuration_with_quota_limiters_different_values( | |
| "enabled": False, | ||
| "model": "cross-encoder/ms-marco-MiniLM-L6-v2", | ||
| }, | ||
| "skills": None, | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -1075,6 +1079,7 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: | |
| "enabled": False, | ||
| "model": "cross-encoder/ms-marco-MiniLM-L6-v2", | ||
| }, | ||
| "skills": None, | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -1282,4 +1287,84 @@ def test_dump_configuration_pg_namespace(tmp_path: Path) -> None: | |
| "enabled": False, | ||
| "model": "cross-encoder/ms-marco-MiniLM-L6-v2", | ||
| }, | ||
| "skills": None, | ||
| } | ||
|
|
||
|
|
||
| def test_dump_configuration_with_skills(tmp_path: Path) -> None: | ||
| """ | ||
| Test that Configuration with skills paths can be serialized to JSON. | ||
|
|
||
| Verifies that skills paths are properly dumped and serialized as strings. | ||
| """ | ||
| cfg = Configuration( | ||
| name="test_name", | ||
| service=ServiceConfiguration( | ||
| tls_config=TLSConfiguration( | ||
| tls_certificate_path=Path("tests/configuration/server.crt"), | ||
| tls_key_path=Path("tests/configuration/server.key"), | ||
| tls_key_password=Path("tests/configuration/password"), | ||
| ), | ||
| cors=CORSConfiguration( | ||
| allow_origins=["foo_origin", "bar_origin", "baz_origin"], | ||
| allow_credentials=False, | ||
| allow_methods=["foo_method", "bar_method", "baz_method"], | ||
| allow_headers=["foo_header", "bar_header", "baz_header"], | ||
| ), | ||
| ), | ||
| llama_stack=LlamaStackConfiguration( | ||
| use_as_library_client=True, | ||
| library_client_config_path="tests/configuration/run.yaml", | ||
| api_key=SecretStr("whatever"), | ||
| ), | ||
| user_data_collection=UserDataCollection( | ||
| feedback_enabled=False, feedback_storage=None | ||
| ), | ||
| database=DatabaseConfiguration( | ||
| sqlite=None, | ||
| postgres=PostgreSQLDatabaseConfiguration( | ||
| db="lightspeed_stack", | ||
| user="ls_user", | ||
| password=SecretStr("ls_password"), | ||
| port=5432, | ||
| ca_cert_path=None, | ||
| ssl_mode="require", | ||
| gss_encmode="disable", | ||
| ), | ||
| ), | ||
| mcp_servers=[], | ||
| customization=None, | ||
| inference=InferenceConfiguration( | ||
| default_provider="default_provider", | ||
| default_model="default_model", | ||
| ), | ||
| skills=SkillsConfiguration( | ||
| paths=[ | ||
| "/var/skills/openshift-troubleshooting", | ||
| "/var/skills/code-review", | ||
| "/opt/custom-skills", | ||
| ] | ||
| ), | ||
| ) | ||
| assert cfg is not None | ||
| dump_file = tmp_path / "test.json" | ||
| cfg.dump(dump_file) | ||
|
|
||
| with open(dump_file, "r", encoding="utf-8") as fin: | ||
| content = json.load(fin) | ||
| # content should be loaded | ||
| assert content is not None | ||
|
|
||
| # skills section must exist | ||
| assert "skills" in content | ||
| assert content["skills"] is not None | ||
| assert "paths" in content["skills"] | ||
|
|
||
| # verify skills paths are properly serialized | ||
| assert content["skills"] == { | ||
| "paths": [ | ||
| "/var/skills/openshift-troubleshooting", | ||
| "/var/skills/code-review", | ||
| "/opt/custom-skills", | ||
| ] | ||
| } | ||
|
Comment on lines
+1294
to
1370
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win Inconsistent test pattern: partial assertion instead of full configuration. All other dump tests in this file ( Consider adding a complete JSON assertion like the other tests, verifying all expected sections exist and match their expected values. 📋 Suggested structure to match existing test patternAfter line 1357, before the skills-specific assertions, add: assert content is not None
+ # all sections must exist
+ assert "name" in content
+ assert "service" in content
+ assert "llama_stack" in content
+ assert "user_data_collection" in content
+ assert "mcp_servers" in content
+ assert "authentication" in content
+ assert "authorization" in content
+ assert "customization" in content
+ assert "inference" in content
+ assert "database" in content
+ assert "byok_rag" in content
+ assert "quota_handlers" in content
+ assert "azure_entra_id" in content
+ assert "reranker" in content
+
# skills section must exist
assert "skills" in contentThen replace the partial assertion (lines 1364-1370) with a complete JSON comparison following the pattern from 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| """Unit tests for SkillsConfiguration model.""" | ||
|
|
||
| # pylint: disable=no-member | ||
| # Pydantic Field(default_factory=...) pattern confuses pylint's static analysis | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from pydantic import ValidationError | ||
|
|
||
| from models.config import SkillsConfiguration | ||
|
|
||
|
|
||
| class TestSkillsConfiguration: | ||
| """Tests for SkillsConfiguration model.""" | ||
|
|
||
| def test_empty_paths_list(self) -> None: | ||
| """Test that an explicit empty paths list is allowed.""" | ||
| config = SkillsConfiguration(paths=[]) | ||
| assert config.paths == [] | ||
|
|
||
| def test_no_unknown_fields_allowed(self) -> None: | ||
| """Test that SkillsConfiguration rejects unknown fields.""" | ||
| with pytest.raises(ValidationError, match="Extra inputs are not permitted"): | ||
| SkillsConfiguration(unknown_field="value") # type: ignore[call-arg] | ||
|
|
||
| def test_skill_paths(self) -> None: | ||
| """Test configuration with multiple skill paths.""" | ||
| config = SkillsConfiguration( | ||
| paths=[ | ||
| "/var/skills/openshift-troubleshooting", | ||
| "/var/skills/code-review", | ||
| "/opt/custom-skills", | ||
| ] | ||
| ) | ||
| assert len(config.paths) == 3 | ||
| assert Path("/var/skills/openshift-troubleshooting") in config.paths | ||
| assert Path("/var/skills/code-review") in config.paths | ||
| assert Path("/opt/custom-skills") in config.paths | ||
|
|
||
| def test_mixed_absolute_and_relative_paths(self) -> None: | ||
| """Test that both absolute and relative paths can be mixed.""" | ||
| config = SkillsConfiguration( | ||
| paths=["/var/skills", "./local-skills", "/opt/skills"] | ||
| ) | ||
| assert len(config.paths) == 3 | ||
| assert Path("/var/skills") in config.paths | ||
| assert Path("./local-skills") in config.paths | ||
| assert Path("/opt/skills") in config.paths |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Consider adding
minItems: 1constraint to thepathsarray.An empty
pathsarray would make theSkillsConfigurationobject effectively useless. Adding aminItems: 1constraint would enforce that at least one skill path is provided.📐 Proposed refinement
"paths": { "items": { "type": "string" }, "type": "array", + "minItems": 1, "title": "Skill paths", "description": "Paths to skill directories or directories containing skill subdirectories." }📝 Committable suggestion
🤖 Prompt for AI Agents