Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Integrate Kling AI video generation into CI/CD pipelines. Use when automating video content in GitHub Actions or GitLab CI. Trigger with phrases like 'klingai ci', 'kling ai github actions', 'klingai automation', 'automated video generation'.
.claude/skills/jeremylongshore-klingai-ci-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 54% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 7% | 0% |
| case-15 | ✗→✓ | ▲ Improved | -16% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 20% | 0% |
| case-10 | ✓→✗ | ▼ Worse | 82% | 0% |
Automate video generation in CI/CD pipelines. Common use cases: generate product demos on release, create marketing videos from prompts in a YAML file, regression-test video quality across model versions.
yaml# .github/workflows/generate-videos.yml name: Generate Videos on: workflow_dispatch: inputs: prompt: description: "Video prompt" required: true model: description: "Model version" default: "kling-v2-master" jobs: generate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install dependencies run: pip install PyJWT requests - name: Generate video env: KLING_ACCESS_KEY: ${{ secrets.KLING_ACCESS_KEY }} KLING_SECRET_KEY: ${{ secrets.KLING_SECRET_KEY }} run: | python3 scripts/generate-video.py \ --prompt "${{ inputs.prompt }}" \ --model "${{ inputs.model }}" \ --output output/ - name: Upload artifact uses: actions/upload-artifact@v4 with: name: generated-video path: output/*.mp4 retention-days: 7
python#!/usr/bin/env python3 """scripts/generate-video.py -- CI-friendly video generation.""" import argparse import jwt import time import os import requests import sys BASE = "https://api.klingai.com/v1" def get_headers(): ak, sk = os.environ["KLING_ACCESS_KEY"], os.environ["KLING_SECRET_KEY"] token = jwt.encode( {"iss": ak, "exp": int(time.time()) + 1800, "nbf": int(time.time()) - 5}, sk, algorithm="HS256", headers={"alg": "HS256", "typ": "JWT"} ) return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} def main(): parser = argparse.ArgumentParser() parser.add_argument("--prompt", required=True) parser.add_argument("--model", default="kling-v2-master") parser.add_argument("--duration", default="5") parser.add_argument("--mode", default="standard") parser.add_argument("--output", default="output/") parser.add_argument("--timeout", type=int, default=600) args = parser.parse_args() os.makedirs(args.output, exist_ok=True) # Submit r = requests.post(f"{BASE}/videos/text2video", headers=get_headers(), json={ "model_name": args.model, "prompt": args.prompt, "duration": args.duration, "mode": args.mode, }) r.raise_for_status() task_id = r.json()["data"]["task_id"] print(f"Task submitted: {task_id}") # Poll start = time.monotonic() while time.monotonic() - start < args.timeout: time.sleep(15) result = requests.get( f"{BASE}/videos/text2video/{task_id}", headers=get_headers() ).json() status = result["data"]["task_status"] elapsed = int(time.monotonic() - start) print(f"[{elapsed}s] Status: {status}") if status == "succeed": video_url = result["data"]["task_result"]["videos"][0]["url"] filepath = os.path.join(args.output, f"{task_id}.mp4") with open(filepath, "wb") as f: f.write(requests.get(video_url).content) print(f"Saved: {filepath}") return if status == "failed": print(f"FAILED: {result['data'].get('task_status_msg')}", file=sys.stderr) sys.exit(1) print("TIMEOUT: generation did not complete", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()
yaml# video-prompts.yml videos: - name: product-hero prompt: "Sleek laptop floating in space with particle effects" model: kling-v2-6 mode: professional - name: feature-demo prompt: "Dashboard interface morphing between screens" model: kling-v2-5-turbo mode: standard
pythonimport yaml with open("video-prompts.yml") as f: config = yaml.safe_load(f) for video in config["videos"]: task_id = submit_async(video["prompt"], model=video["model"]) print(f"{video['name']}: {task_id}")
yaml# .gitlab-ci.yml generate-video: image: python:3.11-slim stage: build script: - pip install PyJWT requests - python3 scripts/generate-video.py --prompt "$VIDEO_PROMPT" --output output/ artifacts: paths: - output/*.mp4 expire_in: 7 days variables: KLING_ACCESS_KEY: $KLING_ACCESS_KEY KLING_SECRET_KEY: $KLING_SECRET_KEY
| Platform | Store AK/SK in | |----------|---------------| | GitHub Actions | Repository Secrets | | GitLab CI | CI/CD Variables (masked) | | AWS CodeBuild | Parameter Store / Secrets Manager | | GCP Cloud Build | Secret Manager |
Never put API keys in the workflow YAML or commit them to the repo.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 20,951 | 22,075 | +5% | 1 | 1 | 0% | 4,350 | 5,711 | +31% | 0 | 0 | — |
case-02 | fail→fail | 28,288 | 22,397 | -21% | 1 | 1 | 0% | 4,191 | 4,971 | +19% | 0 | 0 | — |
case-03 | fail→fail | 27,313 | 29,230 | +7% | 1 | 1 | 0% | 4,145 | 6,314 | +52% | 0 | 0 | — |
case-04 | pass→pass | 11,167 | 14,482 | +30% | 1 | 1 | 0% | 2,265 | 3,460 | +53% | 0 | 0 | — |
case-22 | pass→pass | 12,263 | 16,502 | +35% | 1 | 1 | 0% | 2,317 | 3,837 | +66% | 0 | 0 | — |
case-05 | pass→pass | 15,847 | 10,355 | -35% | 1 | 1 | 0% | 1,719 | 2,204 | +28% | 0 | 0 | — |
case-06 | fail→pass | 20,653 | 33,982 | +65% | 1 | 1 | 0% | 3,228 | 4,980 | +54% | 0 | 0 | — |
case-07 | pass→pass | 7,142 | 2,314 | -68% | 1 | 1 | 0% | 1,214 | 1,867 | +54% | 0 | 0 | — |
case-08 | pass→pass | 10,404 | 7,515 | -28% | 1 | 1 | 0% | 926 | 1,947 | +110% | 0 | 0 | — |
case-23 | pass→pass | 7,374 | 10,383 | +41% | 1 | 1 | 0% | 1,252 | 2,504 | +100% | 0 | 0 | — |
case-09 | pass→pass | 8,628 | 10,871 | +26% | 1 | 1 | 0% | 1,583 | 2,656 | +68% | 0 | 0 | — |
case-10 | pass→fail | 10,832 | 1,876 | -83% | 1 | 1 | 0% | 995 | 1,809 | +82% | 0 | 0 | — |
case-11 | pass→pass | 11,563 | 11,906 | +3% | 1 | 1 | 0% | 1,723 | 2,550 | +48% | 0 | 0 | — |
case-12 | pass→pass | 12,824 | 8,511 | -34% | 1 | 1 | 0% | 1,879 | 2,095 | +11% | 0 | 0 | — |
case-13 | pass→pass | 27,633 | 7,754 | -72% | 1 | 1 | 0% | 1,715 | 1,911 | +11% | 0 | 0 | — |
case-14 | fail→pass | 17,455 | 9,840 | -44% | 1 | 1 | 0% | 1,993 | 2,132 | +7% | 0 | 0 | — |
case-15 | fail→pass | 17,009 | 2,370 | -86% | 1 | 1 | 0% | 2,306 | 1,940 | -16% | 0 | 0 | — |
case-16 | pass→pass | 11,632 | 15,592 | +34% | 1 | 1 | 0% | 2,121 | 3,615 | +70% | 0 | 0 | — |
case-17 | pass→pass | 14,879 | 18,355 | +23% | 1 | 1 | 0% | 1,839 | 3,537 | +92% | 0 | 0 | — |
case-18 | fail→fail | 18,275 | 12,661 | -31% | 1 | 1 | 0% | 2,017 | 4,213 | +109% | 0 | 0 | — |
case-19 | fail→pass | 21,201 | 3,194 | -85% | 1 | 1 | 0% | 1,730 | 2,069 | +20% | 0 | 0 | — |
case-20 | pass→pass | 10,593 | 7,399 | -30% | 1 | 1 | 0% | 1,966 | 1,917 | -2% | 0 | 0 | — |
case-21 | pass→pass | 12,771 | 11,647 | -9% | 1 | 1 | 0% | 2,686 | 3,935 | +47% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +13 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.