Config: Move config file generation to tabby_config

Keep the models as a separate reference file without any extra
functions.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2024-09-16 22:22:24 -04:00
parent d2d07ed92d
commit 46f9fff210
3 changed files with 80 additions and 84 deletions

View file

@ -1,8 +1,7 @@
import json
from loguru import logger
from common.config_models import generate_config_file
from common.tabby_config import config
from common.tabby_config import config, generate_config_file
from endpoints.server import export_openapi
@ -18,10 +17,8 @@ def branch_to_actions() -> bool:
"Successfully wrote OpenAPI spec to "
+ f"{config.actions.openapi_export_path}"
)
elif config.actions.export_config:
generate_config_file(filename=config.actions.config_export_path)
else:
# did not branch
return False

View file

@ -1,11 +1,7 @@
from inspect import getdoc
from pathlib import Path
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
from pydantic_core import PydanticUndefined
from textwrap import dedent
from typing import List, Literal, Optional, Union
from common.utils import unwrap
CACHE_SIZES = Literal["FP16", "Q8", "Q6", "Q4"]
@ -468,78 +464,3 @@ class TabbyConfigModel(BaseModel):
)
model_config = ConfigDict(validate_assignment=True, protected_namespaces=())
# TODO: Possibly switch to ruamel.yaml for a more native implementation
def generate_config_file(
model: BaseConfigModel = None,
filename: str = "config_sample.yml",
indentation: int = 2,
) -> None:
"""Creates a config.yml file from Pydantic models."""
# Add a preamble
yaml = dedent("""
# Sample YAML file for configuration.
# Comment and uncomment values as needed.
# Every value has a default within the application.
# This file serves to be a drop in for config.yml
# Unless specified in the comments, DO NOT put these options in quotes!
# You can use https://www.yamllint.com/ if you want to check your YAML formatting.\n
""")
schema = unwrap(model, TabbyConfigModel())
# TODO: Make the disordered iteration look cleaner
iter_once = False
for field, field_data in schema.model_fields.items():
# Fetch from the existing model class if it's passed
# Probably can use this on schema too, but play it safe
if model and hasattr(model, field):
subfield_model = getattr(model, field)
else:
subfield_model = field_data.default_factory()
if not subfield_model._metadata.include_in_config:
continue
# Since the list is out of order with the length
# Add newlines from the beginning once one iteration finishes
# This is a sanity check for formatting
if iter_once:
yaml += "\n"
else:
iter_once = True
for line in getdoc(subfield_model).splitlines():
yaml += f"# {line}\n"
yaml += f"{field}:\n"
sub_iter_once = False
for subfield, subfield_data in subfield_model.model_fields.items():
# Same logic as iter_once
if sub_iter_once:
yaml += "\n"
else:
sub_iter_once = True
# If a value already exists, use it
if hasattr(subfield_model, subfield):
value = getattr(subfield_model, subfield)
elif subfield_data.default_factory:
value = subfield_data.default_factory()
else:
value = subfield_data.default
value = value if value is not None else ""
value = value if value is not PydanticUndefined else ""
for line in subfield_data.description.splitlines():
yaml += f"{' ' * indentation}# {line}\n"
yaml += f"{' ' * indentation}{subfield}: {value}\n"
with open(filename, "w") as f:
f.write(yaml)

View file

@ -1,11 +1,14 @@
import yaml
import pathlib
from inspect import getdoc
from pydantic_core import PydanticUndefined
from loguru import logger
from textwrap import dedent
from typing import Optional
from os import getenv
from common.utils import unwrap, merge_dicts
from common.config_models import TabbyConfigModel, generate_config_file
from common.config_models import BaseConfigModel, TabbyConfigModel
class TabbyConfig(TabbyConfigModel):
@ -159,3 +162,78 @@ class TabbyConfig(TabbyConfigModel):
# Create an empty instance of the config class
config: TabbyConfig = TabbyConfig()
# TODO: Possibly switch to ruamel.yaml for a more native implementation
def generate_config_file(
model: BaseConfigModel = None,
filename: str = "config_sample.yml",
indentation: int = 2,
) -> None:
"""Creates a config.yml file from Pydantic models."""
# Add a preamble
yaml = dedent("""
# Sample YAML file for configuration.
# Comment and uncomment values as needed.
# Every value has a default within the application.
# This file serves to be a drop in for config.yml
# Unless specified in the comments, DO NOT put these options in quotes!
# You can use https://www.yamllint.com/ if you want to check your YAML formatting.\n
""")
schema = unwrap(model, TabbyConfigModel())
# TODO: Make the disordered iteration look cleaner
iter_once = False
for field, field_data in schema.model_fields.items():
# Fetch from the existing model class if it's passed
# Probably can use this on schema too, but play it safe
if model and hasattr(model, field):
subfield_model = getattr(model, field)
else:
subfield_model = field_data.default_factory()
if not subfield_model._metadata.include_in_config:
continue
# Since the list is out of order with the length
# Add newlines from the beginning once one iteration finishes
# This is a sanity check for formatting
if iter_once:
yaml += "\n"
else:
iter_once = True
for line in getdoc(subfield_model).splitlines():
yaml += f"# {line}\n"
yaml += f"{field}:\n"
sub_iter_once = False
for subfield, subfield_data in subfield_model.model_fields.items():
# Same logic as iter_once
if sub_iter_once:
yaml += "\n"
else:
sub_iter_once = True
# If a value already exists, use it
if hasattr(subfield_model, subfield):
value = getattr(subfield_model, subfield)
elif subfield_data.default_factory:
value = subfield_data.default_factory()
else:
value = subfield_data.default
value = value if value is not None else ""
value = value if value is not PydanticUndefined else ""
for line in subfield_data.description.splitlines():
yaml += f"{' ' * indentation}# {line}\n"
yaml += f"{' ' * indentation}{subfield}: {value}\n"
with open(filename, "w") as f:
f.write(yaml)