Skip to content

Commit c4a3e40

Browse files
authored
Merge pull request #2294 from yangcao77/list-skills-endpoint
RHIDP-15891: add /v1/skills endpoint
2 parents f7d9bd7 + c355afc commit c4a3e40

22 files changed

Lines changed: 766 additions & 5 deletions

File tree

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,57 @@ will be returned.
12641264
}
12651265
```
12661266
1267+
## Skills endpoint
1268+
1269+
**Endpoint:** `GET /v1/skills`
1270+
1271+
Process GET requests and return the list of agent skills loaded from the
1272+
directories configured under `skills.paths` in the service configuration
1273+
(see [Agent Skills](#agent-skills) and the [Agent Skills Guide](docs/user_doc/skills_guide.md)
1274+
for configuration and authoring instructions). Each skill's name and
1275+
description are read from its `SKILL.md` frontmatter.
1276+
1277+
This endpoint reads the configured skill directories directly and does not
1278+
invoke an LLM or agent — it is intended for clients (e.g. the RHDH UI or
1279+
other tooling) that need a deterministic way to introspect configured
1280+
skills without the cost, latency, or non-determinism of an LLM tool call.
1281+
This is distinct from the `list_skills` tool that the agent itself may
1282+
invoke during a `/v1/query` or `/v1/streaming_query` turn.
1283+
1284+
If [authentication](#authentication) is enabled, include the appropriate
1285+
credentials; otherwise the request returns `401`/`403`.
1286+
1287+
```bash
1288+
curl -H "Authorization: Bearer <token>" \
1289+
http://localhost:8080/v1/skills
1290+
```
1291+
1292+
**Response Body:**
1293+
1294+
```json
1295+
{
1296+
"skills": [
1297+
{
1298+
"name": "code-review",
1299+
"description": "Review code for quality and security"
1300+
},
1301+
{
1302+
"name": "openshift-troubleshooting",
1303+
"description": "Troubleshoot OpenShift cluster issues"
1304+
}
1305+
]
1306+
}
1307+
```
1308+
1309+
If no skills are configured (or `skills.paths` is empty), the endpoint
1310+
returns an empty list:
1311+
1312+
```json
1313+
{
1314+
"skills": []
1315+
}
1316+
```
1317+
12671318
12681319
# Database structure
12691320

docs/devel_doc/ARCHITECTURE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ This section documents the REST API endpoints exposed by LCore for client intera
506506
**List Shields:** `GET /v1/shields`
507507
- Returns list of shields configured in LCORE
508508

509+
**List Skills:** `GET /skills`
510+
- Returns loaded agent skills (name and description) from the configured
511+
skill directories, without requiring an LLM/agent turn
512+
509513
**List RAG Databases:** `GET /rags`
510514
- Returns configured vector stores
511515

docs/devel_doc/openapi.json

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,156 @@
15841584
}
15851585
}
15861586
},
1587+
"/v1/skills": {
1588+
"get": {
1589+
"tags": [
1590+
"skills"
1591+
],
1592+
"summary": "Skills Endpoint Handler",
1593+
"description": "Handle requests to the /skills endpoint.\n\nProcess GET requests to the /skills endpoint, returning a list of loaded\nagent skills with their metadata (name, description).\n\n### Parameters:\n- request: The incoming HTTP request (used by middleware).\n- auth: Authentication tuple from the auth dependency (used by middleware).\n\n### Raises:\n- HTTPException: with status 401 for unauthorized access.\n- HTTPException: with status 403 if permission is denied.\n- HTTPException: with status 500 and a detail object containing `response`\n and `cause` when service configuration is wrong or incomplete.\n\n### Returns:\n- SkillsResponse: An object containing the list of loaded skills.",
1594+
"operationId": "skills_endpoint_handler_v1_skills_get",
1595+
"responses": {
1596+
"200": {
1597+
"description": "Successful response",
1598+
"content": {
1599+
"application/json": {
1600+
"schema": {
1601+
"$ref": "#/components/schemas/SkillsResponse"
1602+
},
1603+
"example": {
1604+
"skills": [
1605+
{
1606+
"description": "Review code for quality and security",
1607+
"name": "code-review"
1608+
},
1609+
{
1610+
"description": "Troubleshoot OpenShift cluster issues",
1611+
"name": "openshift-troubleshooting"
1612+
}
1613+
]
1614+
}
1615+
}
1616+
}
1617+
},
1618+
"401": {
1619+
"description": "Unauthorized",
1620+
"content": {
1621+
"application/json": {
1622+
"schema": {
1623+
"$ref": "#/components/schemas/UnauthorizedResponse"
1624+
},
1625+
"examples": {
1626+
"missing header": {
1627+
"value": {
1628+
"detail": {
1629+
"cause": "No Authorization header found",
1630+
"response": "Missing or invalid credentials provided by client"
1631+
}
1632+
}
1633+
},
1634+
"missing token": {
1635+
"value": {
1636+
"detail": {
1637+
"cause": "No token found in Authorization header",
1638+
"response": "Missing or invalid credentials provided by client"
1639+
}
1640+
}
1641+
},
1642+
"expired token": {
1643+
"value": {
1644+
"detail": {
1645+
"cause": "Token has expired",
1646+
"response": "Missing or invalid credentials provided by client"
1647+
}
1648+
}
1649+
},
1650+
"invalid signature": {
1651+
"value": {
1652+
"detail": {
1653+
"cause": "Invalid token signature",
1654+
"response": "Missing or invalid credentials provided by client"
1655+
}
1656+
}
1657+
},
1658+
"invalid key": {
1659+
"value": {
1660+
"detail": {
1661+
"cause": "Token signed by unknown key",
1662+
"response": "Missing or invalid credentials provided by client"
1663+
}
1664+
}
1665+
},
1666+
"missing claim": {
1667+
"value": {
1668+
"detail": {
1669+
"cause": "Token missing claim: user_id",
1670+
"response": "Missing or invalid credentials provided by client"
1671+
}
1672+
}
1673+
},
1674+
"invalid k8s token": {
1675+
"value": {
1676+
"detail": {
1677+
"cause": "Invalid or expired Kubernetes token",
1678+
"response": "Missing or invalid credentials provided by client"
1679+
}
1680+
}
1681+
},
1682+
"invalid jwk token": {
1683+
"value": {
1684+
"detail": {
1685+
"cause": "Authentication key server returned invalid data",
1686+
"response": "Missing or invalid credentials provided by client"
1687+
}
1688+
}
1689+
}
1690+
}
1691+
}
1692+
}
1693+
},
1694+
"403": {
1695+
"description": "Permission denied",
1696+
"content": {
1697+
"application/json": {
1698+
"schema": {
1699+
"$ref": "#/components/schemas/ForbiddenResponse"
1700+
},
1701+
"examples": {
1702+
"endpoint": {
1703+
"value": {
1704+
"detail": {
1705+
"cause": "User 6789 is not authorized to access this endpoint.",
1706+
"response": "User does not have permission to access this endpoint"
1707+
}
1708+
}
1709+
}
1710+
}
1711+
}
1712+
}
1713+
},
1714+
"500": {
1715+
"description": "Internal server error",
1716+
"content": {
1717+
"application/json": {
1718+
"schema": {
1719+
"$ref": "#/components/schemas/InternalServerErrorResponse"
1720+
},
1721+
"examples": {
1722+
"configuration": {
1723+
"value": {
1724+
"detail": {
1725+
"cause": "Lightspeed Stack configuration has not been initialized.",
1726+
"response": "Configuration is not loaded"
1727+
}
1728+
}
1729+
}
1730+
}
1731+
}
1732+
}
1733+
}
1734+
}
1735+
}
1736+
},
15871737
"/v1/providers": {
15881738
"get": {
15891739
"tags": [
@@ -11568,6 +11718,7 @@
1156811718
"feedback",
1156911719
"get_models",
1157011720
"get_tools",
11721+
"get_skills",
1157111722
"get_shields",
1157211723
"list_providers",
1157311724
"get_provider",
@@ -21257,6 +21408,27 @@
2125721408
}
2125821409
]
2125921410
},
21411+
"SkillMetadata": {
21412+
"properties": {
21413+
"name": {
21414+
"type": "string",
21415+
"title": "Name",
21416+
"description": "Unique name of the skill"
21417+
},
21418+
"description": {
21419+
"type": "string",
21420+
"title": "Description",
21421+
"description": "Human readable description of what the skill does"
21422+
}
21423+
},
21424+
"type": "object",
21425+
"required": [
21426+
"name",
21427+
"description"
21428+
],
21429+
"title": "SkillMetadata",
21430+
"description": "Metadata describing a single loaded agent skill.\n\nAttributes:\n name: Unique name of the skill.\n description: Human readable description of what the skill does."
21431+
},
2126021432
"SkillsConfiguration": {
2126121433
"properties": {
2126221434
"paths": {
@@ -21274,6 +21446,38 @@
2127421446
"title": "SkillsConfiguration",
2127521447
"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."
2127621448
},
21449+
"SkillsResponse": {
21450+
"properties": {
21451+
"skills": {
21452+
"items": {
21453+
"$ref": "#/components/schemas/SkillMetadata"
21454+
},
21455+
"type": "array",
21456+
"title": "Skills",
21457+
"description": "List of loaded skills with metadata"
21458+
}
21459+
},
21460+
"type": "object",
21461+
"required": [
21462+
"skills"
21463+
],
21464+
"title": "SkillsResponse",
21465+
"description": "Model representing a response to skills request.\n\nAttributes:\n skills: List of loaded skills with metadata (name and description).",
21466+
"examples": [
21467+
{
21468+
"skills": [
21469+
{
21470+
"description": "Review code for quality and security",
21471+
"name": "code-review"
21472+
},
21473+
{
21474+
"description": "Troubleshoot OpenShift cluster issues",
21475+
"name": "openshift-troubleshooting"
21476+
}
21477+
]
21478+
}
21479+
]
21480+
},
2127721481
"SolrVectorSearchRequest": {
2127821482
"properties": {
2127921483
"mode": {
@@ -22853,6 +23057,10 @@
2285323057
"name": "shields",
2285423058
"description": "Safety shields."
2285523059
},
23060+
{
23061+
"name": "skills",
23062+
"description": "Agent skills."
23063+
},
2285623064
{
2285723065
"name": "streaming_query",
2285823066
"description": "Streaming query (SSE)."

docs/models/successful_responses.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2535,6 +2535,22 @@ Model representing a response to shields request.
25352535
| shields | array | List of shields configured in Lightspeed Core Stack |
25362536

25372537

2538+
## SkillMetadata
2539+
2540+
2541+
Metadata describing a single loaded agent skill.
2542+
2543+
Attributes:
2544+
name: Unique name of the skill.
2545+
description: Human readable description of what the skill does.
2546+
2547+
2548+
| Field | Type | Description |
2549+
|-------|------|-------------|
2550+
| name | string | Unique name of the skill |
2551+
| description | string | Human readable description of what the skill does |
2552+
2553+
25382554
## SkillsConfiguration
25392555

25402556

@@ -2555,6 +2571,17 @@ Paths are validated at startup to ensure they exist and contain valid SKILL.md f
25552571
| paths | array | Paths to skill directories or directories containing skill subdirectories. |
25562572

25572573

2574+
## SkillsResponse
2575+
2576+
2577+
Model representing a response to skills request.
2578+
2579+
2580+
| Field | Type | Description |
2581+
|-------|------|-------------|
2582+
| skills | array | List of loaded skills with metadata |
2583+
2584+
25582585
## SplunkConfiguration
25592586

25602587

0 commit comments

Comments
 (0)