mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-13 17:10:08 +00:00
[Fix] Use unique skill names in Skills API test to avoid duplicate-name 500s
The test_create_skill test was consistently failing in CI with a 500 from Anthropic because the SKILL.md frontmatter always used the same hardcoded name (test-skill-litellm). Since test_delete_skill is permanently skipped, skills accumulate in the CI account, and re-creating with a duplicate name triggers an Internal Server Error on Anthropic's side. Fix: pass a timestamp-based unique_suffix to create_skill_zip so each run produces a distinct skill name in the zip's SKILL.md frontmatter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,27 +23,42 @@ from litellm.types.llms.anthropic_skills import (
|
||||
|
||||
|
||||
@contextmanager
|
||||
def create_skill_zip(skill_name: str):
|
||||
def create_skill_zip(skill_name: str, unique_suffix: Optional[str] = None):
|
||||
"""
|
||||
Helper context manager to create a zip file for a skill.
|
||||
|
||||
|
||||
Args:
|
||||
skill_name: Name of the skill directory in test_skills_data/
|
||||
|
||||
unique_suffix: Optional suffix to make the skill name unique in the zip.
|
||||
When provided, the SKILL.md frontmatter name is rewritten
|
||||
to avoid duplicate-name conflicts on the API side.
|
||||
|
||||
Yields:
|
||||
File handle to the zip file
|
||||
|
||||
|
||||
The zip file is automatically cleaned up after use.
|
||||
"""
|
||||
import time
|
||||
|
||||
test_dir = Path(__file__).parent / "test_skills_data"
|
||||
skill_dir = test_dir / skill_name
|
||||
|
||||
|
||||
# Create a zip file containing the skill directory
|
||||
zip_path = test_dir / f"{skill_name}.zip"
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
||||
zip_file.write(skill_dir, arcname=skill_name)
|
||||
zip_file.write(skill_dir / "SKILL.md", arcname=f"{skill_name}/SKILL.md")
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
zf.write(skill_dir, arcname=skill_name)
|
||||
|
||||
if unique_suffix is not None:
|
||||
# Rewrite SKILL.md with a unique name to avoid API conflicts
|
||||
skill_md = (skill_dir / "SKILL.md").read_text()
|
||||
skill_md = skill_md.replace(
|
||||
f"name: {skill_name}",
|
||||
f"name: {skill_name}-{unique_suffix}",
|
||||
)
|
||||
zf.writestr(f"{skill_name}/SKILL.md", skill_md)
|
||||
else:
|
||||
zf.write(skill_dir / "SKILL.md", arcname=f"{skill_name}/SKILL.md")
|
||||
|
||||
try:
|
||||
with open(zip_path, "rb") as f:
|
||||
yield f
|
||||
@@ -77,13 +92,13 @@ class BaseSkillsAPITest(ABC):
|
||||
def test_create_skill(self):
|
||||
"""
|
||||
Test creating a skill.
|
||||
|
||||
|
||||
Note: This test creates a skill but does not clean it up,
|
||||
as we want to verify it was created successfully.
|
||||
The test_delete_skill test will handle cleanup.
|
||||
"""
|
||||
import time
|
||||
|
||||
|
||||
custom_llm_provider = self.get_custom_llm_provider()
|
||||
api_key = self.get_api_key()
|
||||
api_base = self.get_api_base()
|
||||
@@ -96,12 +111,14 @@ class BaseSkillsAPITest(ABC):
|
||||
|
||||
# Use helper to create skill zip
|
||||
skill_name = "test-skill-litellm"
|
||||
|
||||
# Use unique title to avoid conflicts with previous test runs
|
||||
unique_title = f"Test Skill {int(time.time())}"
|
||||
|
||||
|
||||
# Use unique title and unique skill name to avoid conflicts
|
||||
# with previous test runs (skills are never cleaned up in CI)
|
||||
ts = str(int(time.time()))
|
||||
unique_title = f"Test Skill {ts}"
|
||||
|
||||
# Upload the skill with the zip file
|
||||
with create_skill_zip(skill_name) as zip_file:
|
||||
with create_skill_zip(skill_name, unique_suffix=ts) as zip_file:
|
||||
response = litellm.create_skill(
|
||||
display_title=unique_title,
|
||||
files=[zip_file],
|
||||
@@ -217,12 +234,13 @@ class BaseSkillsAPITest(ABC):
|
||||
|
||||
# Use helper to create skill zip
|
||||
skill_name = "test-delete-skill"
|
||||
|
||||
# Use unique title to avoid conflicts
|
||||
unique_title = f"Test Delete Skill {int(time.time())}"
|
||||
|
||||
|
||||
# Use unique title and skill name to avoid conflicts
|
||||
ts = str(int(time.time()))
|
||||
unique_title = f"Test Delete Skill {ts}"
|
||||
|
||||
# Create a skill specifically to delete
|
||||
with create_skill_zip(skill_name) as zip_file:
|
||||
with create_skill_zip(skill_name, unique_suffix=ts) as zip_file:
|
||||
created_skill = litellm.create_skill(
|
||||
display_title=unique_title,
|
||||
files=[zip_file],
|
||||
|
||||
Reference in New Issue
Block a user