Initial commit: 25 scripts Python sauvegardés
Date: 2026-08-28 11:43:34 EDT
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
airtable:30f47a4b29827da14e655356c0edd8a7
|
||||
apple-notes:5e448abf984561fb33b197045ce41388
|
||||
apple-reminders:cda2963c73800643faf4a34ef813879a
|
||||
architecture-diagram:ca5e216b2014eef4f38f0a488eaf3545
|
||||
arxiv:06b6666b948852e77545c99ef72139db
|
||||
ascii-art:3aea656d9b8fb9d054ce37565e704a04
|
||||
ascii-video:2c8277458b2ef50421ce44debb9d81ad
|
||||
audiocraft-audio-generation:c207bdbf300ea5c42decc9cb6a596d1c
|
||||
baoyu-comic:8ff68387b01dea27ce049837ae9ecc47
|
||||
baoyu-infographic:53edf7d1b9398d62f4ccb0755e27913e
|
||||
blogwatcher:3f30bdd408c771501b94fab9289579c6
|
||||
claude-code:231f7e3cb0b2b91f64ce4b23fc2cef4d
|
||||
claude-design:c1b63b7651b66fd15d096e57728ec686
|
||||
codebase-inspection:29f67c87df868dd08e76c57b86c7a5c6
|
||||
codex:a116c5f880869f663b2974c2c9a79dbd
|
||||
comfyui:7efa41ae19823ed75a05ce96d9c44e07
|
||||
debugging-hermes-tui-commands:b093ceac961144fb3e72e57b12a5d354
|
||||
design-md:b40264457352831ab1d06f3ec671b532
|
||||
dogfood:ae6e92c2cd27c3da8a0587f089d19fe3
|
||||
dspy:64659f5eba8f127700fa931f58175f01
|
||||
evaluating-llms-harness:ac24cf5202db5b024b3079023797a0f6
|
||||
excalidraw:149a572d2069ee3de2951352725a8b19
|
||||
findmy:1d7dd3ae39cf25357a374c6bfb956442
|
||||
gif-search:d02d7fb065dfc143c079f15b8d8af1a7
|
||||
github-auth:c58654476268579b4cfa5953bb4160d0
|
||||
github-code-review:b771855b5c56b5e3a2546167a51667ba
|
||||
github-issues:429ec06cde7578e4ebba451d8106f008
|
||||
github-pr-workflow:48b6aceaae5333f6d3ed2d72f595331b
|
||||
github-repo-management:c5c05bc85dfcda2b7ac2f2006be9efe0
|
||||
godmode:27f853c53d2a2ef38118a583a9dfb6c4
|
||||
google-workspace:db46b2778030ed92d03a8aab727c036c
|
||||
heartmula:96a5927a5f221065260ddb2e0f1d77ec
|
||||
hermes-agent:166e68c17dec7adf1f91b20f904f251d
|
||||
hermes-agent-skill-authoring:4fefd83beeea0a8a32f5ae006f10f00d
|
||||
himalaya:3f05533dcd15be9d89cc86ab13c94a83
|
||||
huggingface-hub:da338c5152d72db030bb81d923d1c64d
|
||||
humanizer:6645b341862575f452e86139c5c71ce9
|
||||
ideation:8cfdaafa9abda023643427110c8c3073
|
||||
imessage:f545da0f5cc64dd9ee1ffd2b7733a11b
|
||||
jupyter-live-kernel:352c43dc28428592abbc8c91cb5ce295
|
||||
kanban-orchestrator:335a645935c88361ef2a103dba1fba01
|
||||
kanban-worker:381c14fb24d058e1750d1f97aa8d8011
|
||||
linear:58a67824f7df6e4753cfe0575be9e921
|
||||
llama-cpp:0991055ce47146735f0ed02d7658a254
|
||||
llm-wiki:ca957fbfecd9e573a1f99db3937a1969
|
||||
macos-computer-use:fb1787b32be343b87078d2ae2618dbcf
|
||||
manim-video:2ad3d68c3eb5d2675c05138100d3e48b
|
||||
maps:6d94a271a5341dc94789a9c99eb2b1a8
|
||||
minecraft-modpack-server:71e51465a1df193d7505ee47a91b6dbd
|
||||
nano-pdf:6c643bd0cfb0548ff0ddaf367d4da6d1
|
||||
native-mcp:3e758c66efed8f27c2153729d2050c87
|
||||
node-inspect-debugger:55501511963a3a6410fc767b5ed3e21c
|
||||
notion:930f6c7d327dd86553f45e2da9d9901e
|
||||
obliteratus:d3333944ef536dbe9238766f2f626eae
|
||||
obsidian:be02894afa45a908623ad6f9b7594199
|
||||
ocr-and-documents:af5fba9fa8ef003951ff5fe5a0a04adf
|
||||
opencode:d2a166c7f2c74f6e47d548ed1290c458
|
||||
openhue:ce1dd061d7f49d4752a4c0711ad2666c
|
||||
p5js:5f09fa1cb8494c93bc2f5bcbd34a2ead
|
||||
pixel-art:39562dfa3598566fc09280c054ef0ede
|
||||
plan:50c4df52eb6b0932649a444bc9984e0a
|
||||
pokemon-player:db79617ea4a61e0d6727d38402fb1f94
|
||||
polymarket:7644c886e028c229bc8c1f54114c3170
|
||||
popular-web-designs:b3fd685e8fbcf981755609ce98a4eea9
|
||||
powerpoint:00a6eb2ad4b7be22c1eabf6c19158836
|
||||
pretext:f17f2b6211eb81b96e1fc5d48ecc96a4
|
||||
python-debugpy:b87e0abf179c14ea51c7559dc99eb22c
|
||||
requesting-code-review:9944f54e8ba5c294b47ee93f3e8f239d
|
||||
research-paper-writing:a4a5a13107ff94894a4abdb39a5c7a8d
|
||||
segment-anything-model:7f1317da421fb8eada27aeacdbb21d30
|
||||
serving-llms-vllm:92d66ae1f1112924634fcdcff2f86bc7
|
||||
sketch:f8833126112824f6a916c69630cfd042
|
||||
songsee:644dc0f267b6661a3df6c76ce80d7f1f
|
||||
songwriting-and-ai-music:52be403c894c7bd7d6fe70f7eeaf9460
|
||||
spike:8b01bbb339d72d13a8a3214a018969c4
|
||||
spotify:a0ffe21977c14dc8da5dca039de83b0f
|
||||
subagent-driven-development:f434c120ce9cc40fadb9ff970b45c1e7
|
||||
systematic-debugging:55a23d83e936d3c7c04d4e5fa803558e
|
||||
teams-meeting-pipeline:810d4862830d1eb3daccf898f7c5ee85
|
||||
test-driven-development:31fb75eff421db47e527bce28c493df3
|
||||
touchdesigner-mcp:0664ded9138795d5518def4d16037650
|
||||
webhook-subscriptions:b1ffc6d6a3cf3f59ea8eb7dcd178c555
|
||||
weights-and-biases:8f0e1ee92fdf7b42a1dad448176d7c64
|
||||
writing-plans:ebe31eb58f7a09a7cd8fad4d422b9d3b
|
||||
xurl:e44ca3f6818dd7391a9b12ae79d76c16
|
||||
youtube-content:e43a762cd557db987d9ab466a7a60f76
|
||||
yuanbao:7844c287c57b42dccf51127f15e0751b
|
||||
@@ -0,0 +1,185 @@
|
||||
"""Tests for extract_schema.py."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from extract_schema import (
|
||||
extract_schema,
|
||||
find_negative_prompt_node,
|
||||
find_positive_prompt_node,
|
||||
trace_to_node,
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Connection tracing
|
||||
# =============================================================================
|
||||
|
||||
class TestConnectionTracing:
|
||||
def test_direct_link(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": "x"}},
|
||||
"2": {"class_type": "KSampler",
|
||||
"inputs": {"positive": ["1", 0], "negative": ["1", 0]}},
|
||||
}
|
||||
assert trace_to_node(wf, ["1", 0]) == "1"
|
||||
|
||||
def test_through_reroute(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": "x"}},
|
||||
"2": {"class_type": "Reroute", "inputs": {"input": ["1", 0]}},
|
||||
"3": {"class_type": "Reroute", "inputs": {"input": ["2", 0]}},
|
||||
}
|
||||
assert trace_to_node(wf, ["3", 0]) == "1"
|
||||
|
||||
def test_circular_safe(self):
|
||||
wf = {
|
||||
"1": {"class_type": "Reroute", "inputs": {"input": ["2", 0]}},
|
||||
"2": {"class_type": "Reroute", "inputs": {"input": ["1", 0]}},
|
||||
}
|
||||
# Should hit max_hops without infinite loop
|
||||
result = trace_to_node(wf, ["1", 0], max_hops=5)
|
||||
assert result in {"1", "2"} # any node, just don't hang
|
||||
|
||||
|
||||
class TestPositiveNegativeDetection:
|
||||
def test_basic(self, sd15_workflow):
|
||||
# In sd15_workflow.json node 6 is positive, node 7 is negative
|
||||
assert find_positive_prompt_node(sd15_workflow) == "6"
|
||||
assert find_negative_prompt_node(sd15_workflow) == "7"
|
||||
|
||||
def test_swapped_order(self):
|
||||
wf = {
|
||||
"3": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["7", 0], "negative": ["6", 0],
|
||||
"model": ["4", 0], "latent_image": ["5", 0],
|
||||
"seed": 1, "steps": 20, "cfg": 7.5,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0,
|
||||
}},
|
||||
"4": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "x"}},
|
||||
"5": {"class_type": "EmptyLatentImage", "inputs": {"width": 512, "height": 512, "batch_size": 1}},
|
||||
"6": {"class_type": "CLIPTextEncode", "inputs": {"text": "ugly", "clip": ["4", 1]}},
|
||||
"7": {"class_type": "CLIPTextEncode", "inputs": {"text": "beautiful", "clip": ["4", 1]}},
|
||||
}
|
||||
# Now 7 is the positive (despite higher node ID)
|
||||
assert find_positive_prompt_node(wf) == "7"
|
||||
assert find_negative_prompt_node(wf) == "6"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Schema extraction
|
||||
# =============================================================================
|
||||
|
||||
class TestExtractSchema:
|
||||
def test_basic_sd15(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
params = schema["parameters"]
|
||||
assert "prompt" in params
|
||||
assert "negative_prompt" in params
|
||||
assert "seed" in params
|
||||
assert "steps" in params
|
||||
assert "cfg" in params
|
||||
assert "width" in params
|
||||
assert "height" in params
|
||||
|
||||
def test_prompt_value_correct(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
# The positive prompt in the example is the landscape one
|
||||
assert "landscape" in schema["parameters"]["prompt"]["value"]
|
||||
assert "ugly" in schema["parameters"]["negative_prompt"]["value"]
|
||||
|
||||
def test_model_dependencies(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
deps = schema["model_dependencies"]
|
||||
ckpts = [d["value"] for d in deps if d["folder"] == "checkpoints"]
|
||||
assert "v1-5-pruned-emaonly.safetensors" in ckpts
|
||||
|
||||
def test_output_nodes(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
assert "9" in schema["output_nodes"]
|
||||
|
||||
def test_summary(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
s = schema["summary"]
|
||||
assert s["has_negative_prompt"] is True
|
||||
assert s["has_seed"] is True
|
||||
assert s["is_video_workflow"] is False
|
||||
assert s["parameter_count"] > 5
|
||||
|
||||
def test_flux_workflow(self, flux_workflow):
|
||||
schema = extract_schema(flux_workflow)
|
||||
# Flux uses RandomNoise for seed
|
||||
assert schema["summary"]["has_seed"] is True
|
||||
# Flux has only positive prompt (no negative encoder)
|
||||
assert schema["summary"]["has_negative_prompt"] is False
|
||||
|
||||
def test_video_detected(self, video_workflow):
|
||||
schema = extract_schema(video_workflow)
|
||||
assert schema["summary"]["is_video_workflow"] is True
|
||||
|
||||
|
||||
class TestEmbeddingDeps:
|
||||
def test_extract_from_prompt(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "x"}},
|
||||
"5": {"class_type": "EmptyLatentImage",
|
||||
"inputs": {"width": 512, "height": 512, "batch_size": 1}},
|
||||
"6": {"class_type": "CLIPTextEncode",
|
||||
"inputs": {
|
||||
"text": "a cat, embedding:goodvibes, embedding:art:1.2",
|
||||
"clip": ["1", 1]
|
||||
}},
|
||||
"7": {"class_type": "CLIPTextEncode",
|
||||
"inputs": {
|
||||
"text": "ugly, embedding:badhands",
|
||||
"clip": ["1", 1]
|
||||
}},
|
||||
"3": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["6", 0], "negative": ["7", 0],
|
||||
"model": ["1", 0], "latent_image": ["5", 0],
|
||||
"seed": 1, "steps": 20, "cfg": 7.5,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0,
|
||||
}},
|
||||
"9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "x", "images": ["3", 0]}},
|
||||
}
|
||||
schema = extract_schema(wf)
|
||||
names = [d["embedding_name"] for d in schema["embedding_dependencies"]]
|
||||
assert sorted(names) == ["art", "badhands", "goodvibes"]
|
||||
|
||||
|
||||
class TestDuplicateDeduplication:
|
||||
def test_two_ksamplers_get_unique_names(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "x"}},
|
||||
"5": {"class_type": "EmptyLatentImage",
|
||||
"inputs": {"width": 512, "height": 512, "batch_size": 1}},
|
||||
"6": {"class_type": "CLIPTextEncode", "inputs": {"text": "a", "clip": ["1", 1]}},
|
||||
"7": {"class_type": "CLIPTextEncode", "inputs": {"text": "b", "clip": ["1", 1]}},
|
||||
"3": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["6", 0], "negative": ["7", 0],
|
||||
"model": ["1", 0], "latent_image": ["5", 0],
|
||||
"seed": 42, "steps": 20, "cfg": 7.5,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0,
|
||||
}},
|
||||
"4": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["6", 0], "negative": ["7", 0],
|
||||
"model": ["1", 0], "latent_image": ["5", 0],
|
||||
"seed": 99, "steps": 30, "cfg": 8.0,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 0.6,
|
||||
}},
|
||||
"9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "x", "images": ["3", 0]}},
|
||||
}
|
||||
schema = extract_schema(wf)
|
||||
params = schema["parameters"]
|
||||
# Both seeds present with disambiguated names
|
||||
seed_keys = [k for k in params if "seed" in k]
|
||||
# Symmetric: both renamed (no bare "seed")
|
||||
assert "seed" not in params
|
||||
assert "seed_3" in params and "seed_4" in params
|
||||
assert params["seed_3"]["value"] == 42
|
||||
assert params["seed_4"]["value"] == 99
|
||||
Reference in New Issue
Block a user