Skip to content

Commit 457e093

Browse files
authored
Migrate old config schemas, fix v2.31.0 regression (#1294)
* add failing test catching #1291 * migrate old config schemas on init
1 parent 0e2c4e4 commit 457e093

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

packages/jupyter-ai/jupyter_ai/config_manager.py

+35-3
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,41 @@ def __init__(
150150
self._init_config()
151151

152152
def _init_config_schema(self):
153-
if not os.path.exists(self.schema_path):
154-
os.makedirs(os.path.dirname(self.schema_path), exist_ok=True)
155-
shutil.copy(OUR_SCHEMA_PATH, self.schema_path)
153+
"""
154+
Initializes `config_schema.json` in the user's data dir whenever the
155+
server extension starts. Users may add custom fields to their config
156+
schema to insert new keys into the Jupyter AI config.
157+
158+
New in v2.31.1: Jupyter AI now merges the user's existing config schema
159+
with Jupyter AI's config schema on init. This prevents validation errors
160+
on missing keys when users upgrade Jupyter AI from an older version.
161+
162+
TODO v3: Remove the ability for users to provide a custom config schema.
163+
This feature is entirely unused as far as I am aware, and we need to
164+
simplify how Jupyter AI handles user configuration in v3 anyways.
165+
"""
166+
167+
# ensure the parent directory has been created
168+
os.makedirs(os.path.dirname(self.schema_path), exist_ok=True)
169+
170+
# read existing_schema
171+
if os.path.exists(self.schema_path):
172+
with open(self.schema_path, encoding="utf-8") as f:
173+
existing_schema = json.load(f)
174+
else:
175+
existing_schema = {}
176+
177+
# read default_schema
178+
with open(OUR_SCHEMA_PATH, encoding="utf-8") as f:
179+
default_schema = json.load(f)
180+
181+
# merge existing_schema into default_schema
182+
# specifying existing_schema as the second argument ensures that
183+
# existing_schema always overrides existing keys in default_schema, i.e.
184+
# this call only adds new keys in default_schema.
185+
schema = Merger.merge(default_schema, existing_schema)
186+
with open(self.schema_path, encoding="utf-8", mode="w") as f:
187+
json.dump(schema, f, indent=self.indentation_depth)
156188

157189
def _init_validator(self) -> None:
158190
with open(OUR_SCHEMA_PATH, encoding="utf-8") as f:

packages/jupyter-ai/jupyter_ai/tests/test_config_manager.py

+39
Original file line numberDiff line numberDiff line change
@@ -508,3 +508,42 @@ def test_config_manager_does_not_write_to_defaults(
508508
)
509509

510510
assert defaults == expected_defaults
511+
512+
513+
def test_config_manager_updates_schema(jp_data_dir, common_cm_kwargs):
514+
"""
515+
Asserts that the ConfigManager adds new keys to the user's config schema
516+
which are present in Jupyter AI's schema on init. Asserts that #1291 does
517+
not occur again in the future.
518+
"""
519+
schema_path = str(jp_data_dir / "config_schema.json")
520+
with open(schema_path, "w") as file:
521+
json.dump(
522+
{
523+
"title": "CUSTOM SCHEMA TITLE",
524+
"$schema": "https://json-schema.org/draft/2020-12/schema",
525+
"$comment": "Default values are sourced from `config_manager.py`.",
526+
"type": "object",
527+
"properties": {
528+
"custom_field": {
529+
"$comment": "Custom field added by some developer.",
530+
"type": ["string", "null"],
531+
"default": None,
532+
"readOnly": False,
533+
},
534+
# missing all other properties in config_schema.json
535+
},
536+
},
537+
file,
538+
)
539+
540+
cm_kwargs = {**common_cm_kwargs, "schema_path": schema_path}
541+
542+
cm = ConfigManager(**cm_kwargs)
543+
with open(schema_path) as f:
544+
new_schema = json.loads(f.read())
545+
assert "custom_field" in new_schema["properties"]
546+
assert "model_provider_id" in new_schema["properties"]
547+
assert "fields" in new_schema["properties"]
548+
assert "embeddings_fields" in new_schema["properties"]
549+
assert "completions_fields" in new_schema["properties"]

0 commit comments

Comments
 (0)