-
-
Notifications
You must be signed in to change notification settings - Fork 417
Load personas dynamically from .jupyter
dir
#1380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+195
−10
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
c464c17
First cut at the PersonaLoader imnplementation, no tests yet.
fperez 241a530
Scope import logic more tightly for persona files.
fperez e3edf80
Add first pass of unit tests for the local persona loader.
fperez 8ca434e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] af2b6a5
Refactor into standalone function
fperez fe69bd8
Make persona_classes an instance attr so it actually reloads per chat…
fperez ff1ad2f
Improve logging so we can debug persona loading issues
fperez 2517286
Ignore files with leading dot
fperez 2790961
Add a bit more logging for local persona loading
fperez d0d97cf
Fix logic to skip . and _ files from the start instead of in the late…
fperez 1f9bcaf
Use log.exception correctly to print tracebacks
fperez 5325ba6
Fix path loading logic so personas can import local resources
fperez 37c2203
fix mypy errors
dlqqq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
""" | ||
Test the local persona manager. | ||
""" | ||
|
||
import tempfile | ||
from pathlib import Path | ||
from unittest.mock import Mock | ||
|
||
import pytest | ||
from jupyter_ai.personas.base_persona import BasePersona, PersonaDefaults | ||
from jupyter_ai.personas.persona_manager import load_from_dir | ||
|
||
|
||
@pytest.fixture | ||
def tmp_persona_dir(): | ||
"""Create a temporary directory for testing LocalPersonaLoader with guaranteed cleanup.""" | ||
with tempfile.TemporaryDirectory() as temp_dir: | ||
yield Path(temp_dir) | ||
|
||
|
||
@pytest.fixture | ||
def mock_logger(): | ||
"""Create a mock logger for testing.""" | ||
return Mock() | ||
|
||
|
||
class TestLoadPersonaClassesFromDirectory: | ||
"""Test cases for load_from_dir function.""" | ||
|
||
def test_empty_directory_returns_empty_list(self, tmp_persona_dir, mock_logger): | ||
"""Test that an empty directory returns an empty list of persona classes.""" | ||
result = load_from_dir(str(tmp_persona_dir), mock_logger) | ||
assert result == [] | ||
|
||
def test_non_persona_file_returns_empty_list(self, tmp_persona_dir, mock_logger): | ||
"""Test that a Python file without persona classes returns an empty list.""" | ||
# Create a file that doesn't contain "persona" in the name | ||
non_persona_file = tmp_persona_dir / "no_personas.py" | ||
non_persona_file.write_text("pass") | ||
|
||
result = load_from_dir(str(tmp_persona_dir), mock_logger) | ||
assert result == [] | ||
|
||
def test_simple_persona_file_returns_persona_class(self, tmp_persona_dir, mock_logger): | ||
"""Test that a file with a BasePersona subclass returns that class.""" | ||
# Create a simple persona file | ||
persona_file = tmp_persona_dir / "simple_personas.py" | ||
persona_content = """ | ||
from jupyter_ai.personas.base_persona import BasePersona | ||
|
||
class TestPersona(BasePersona): | ||
id = "test_persona" | ||
name = "Test Persona" | ||
description = "A simple test persona" | ||
|
||
def process_message(self, message): | ||
pass | ||
""" | ||
persona_file.write_text(persona_content) | ||
|
||
result = load_from_dir(str(tmp_persona_dir), mock_logger) | ||
|
||
assert len(result) == 1 | ||
assert result[0].__name__ == "TestPersona" | ||
assert issubclass(result[0], BasePersona) | ||
|
||
def test_bad_persona_file_returns_empty_list(self, tmp_persona_dir, mock_logger): | ||
"""Test that a file with syntax errors returns empty list.""" | ||
# Create a file with invalid Python code | ||
bad_persona_file = tmp_persona_dir / "bad_persona.py" | ||
bad_persona_file.write_text("1/0") | ||
|
||
result = load_from_dir(str(tmp_persona_dir), mock_logger) | ||
|
||
assert result == [] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want a private function that takes a
py_file
and returns the persona class or None. When we start to cache and enable reloading, we may want that encapsulation (this can wait until we really need it).