feat: add content target type for writing literal text to files
Content targets write a string directly to the output file without
invoking any AI provider. They don't require API keys and are not
archived when overwritten.
Example usage in .hokusai.yaml:
file.txt:
content: ABC
This commit is contained in:
parent
fda28b5afa
commit
bb03975ece
5 changed files with 163 additions and 10 deletions
|
|
@ -12,7 +12,12 @@ from pathlib import Path
|
|||
import httpx
|
||||
|
||||
from hokusai.archive import archive_file
|
||||
from hokusai.config import DownloadTargetConfig, GenerateTargetConfig, ProjectConfig
|
||||
from hokusai.config import (
|
||||
ContentTargetConfig,
|
||||
DownloadTargetConfig,
|
||||
GenerateTargetConfig,
|
||||
ProjectConfig,
|
||||
)
|
||||
from hokusai.graph import build_graph, get_build_order, get_subgraph_for_target
|
||||
from hokusai.prompt import extract_placeholder_files, resolve_prompt
|
||||
from hokusai.providers import Provider
|
||||
|
|
@ -151,12 +156,17 @@ async def _build_single_target(
|
|||
# Ensure parent directories exist for targets in subfolders.
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
target_cfg = config.targets[target_name]
|
||||
|
||||
# Content targets write literal text — no archiving, no provider needed.
|
||||
if isinstance(target_cfg, ContentTargetConfig):
|
||||
_ = output_path.write_text(target_cfg.content)
|
||||
return
|
||||
|
||||
# Archive the existing artifact before overwriting.
|
||||
if config.archive_folder is not None:
|
||||
_ = archive_file(output_path, project_dir, config.archive_folder)
|
||||
|
||||
target_cfg = config.targets[target_name]
|
||||
|
||||
if isinstance(target_cfg, DownloadTargetConfig):
|
||||
await _download_target(target_name, target_cfg, project_dir)
|
||||
return
|
||||
|
|
@ -267,6 +277,14 @@ def _is_dirty(
|
|||
"""Check if a target needs rebuilding."""
|
||||
target_cfg = config.targets[target_name]
|
||||
|
||||
if isinstance(target_cfg, ContentTargetConfig):
|
||||
return is_target_dirty(
|
||||
target_name,
|
||||
content=target_cfg.content,
|
||||
state=state,
|
||||
project_dir=project_dir,
|
||||
)
|
||||
|
||||
if isinstance(target_cfg, DownloadTargetConfig):
|
||||
return is_target_dirty(
|
||||
target_name,
|
||||
|
|
@ -300,7 +318,7 @@ def _has_provider(
|
|||
) -> bool:
|
||||
"""Check that the required provider is available; record failure if not."""
|
||||
target_cfg = config.targets[target_name]
|
||||
if isinstance(target_cfg, DownloadTargetConfig):
|
||||
if isinstance(target_cfg, (DownloadTargetConfig, ContentTargetConfig)):
|
||||
return True
|
||||
model_info = resolve_model(target_name, target_cfg, config.defaults)
|
||||
if model_info.name not in provider_index:
|
||||
|
|
@ -345,7 +363,14 @@ def _process_outcomes(
|
|||
else:
|
||||
target_cfg = config.targets[name]
|
||||
|
||||
if isinstance(target_cfg, DownloadTargetConfig):
|
||||
if isinstance(target_cfg, ContentTargetConfig):
|
||||
record_target_state(
|
||||
name,
|
||||
content=target_cfg.content,
|
||||
state=state,
|
||||
project_dir=project_dir,
|
||||
)
|
||||
elif isinstance(target_cfg, DownloadTargetConfig):
|
||||
record_target_state(
|
||||
name,
|
||||
download=target_cfg.download,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue