diff --git a/.github/workflows/jac-gpt-release.yml b/.github/workflows/jac-gpt-release.yml new file mode 100644 index 0000000..44bb657 --- /dev/null +++ b/.github/workflows/jac-gpt-release.yml @@ -0,0 +1,73 @@ +name: Release on Docs Sync + +on: + workflow_run: + workflows: ["Sync Jaseci Docs"] + types: + - completed + branches: + - jac-gpt_optimization + +jobs: + create-release: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Get latest tag + id: get_tag + run: | + git fetch --tags + LATEST_TAG=$(git tag -l "jac-gpt-v*" | sort -V | tail -n 1) + if [ -z "$LATEST_TAG" ]; then + LATEST_TAG="jac-gpt-v0.0.0" + fi + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "Found latest tag: $LATEST_TAG" + + - name: Bump version + id: bump_version + run: | + LATEST_TAG=${{ steps.get_tag.outputs.latest_tag }} + # Extract version number: jac-gpt-v0.6.8 -> 0.6.8 + VERSION=${LATEST_TAG#jac-gpt-v} + + IFS='.' read -ra PARTS <<< "$VERSION" + MAJOR=${PARTS[0]:-0} + MINOR=${PARTS[1]:-0} + PATCH=${PARTS[2]:-0} + + # Always bump patch version + PATCH=$((PATCH + 1)) + + NEW_VERSION="jac-gpt-v$MAJOR.$MINOR.$PATCH" + echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "Creating release: $NEW_VERSION (bumped patch from $LATEST_TAG)" + + - name: Create Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.bump_version.outputs.new_version }} + release_name: Release ${{ steps.bump_version.outputs.new_version }} + body: | + ## JAC-GPT Release ${{ steps.bump_version.outputs.new_version }} + + πŸ“š Automated release triggered by documentation sync + + ### Changes + - Updated documentation from Jaseci repository + + ### Deployment + This release will automatically trigger Docker image builds and deployment to: + - **Backend API**: https://jac-gpt-api.jaseci.org + - **Frontend**: https://jac-gpt.jaseci.org + draft: false + prerelease: false diff --git a/jac-gpt/.gitignore b/jac-gpt/.gitignore index 656cc33..a496af3 100644 --- a/jac-gpt/.gitignore +++ b/jac-gpt/.gitignore @@ -3,4 +3,5 @@ server/mydatabase/ __pycache__/ *.bin *.sqlite3 -*.pickle \ No newline at end of file +*.pickle +!test_dataset.json \ No newline at end of file diff --git a/jac-gpt/server/docs/assets/examples/basic/assignments.jac b/jac-gpt/server/docs/assets/examples/basic/assignments.jac index 5989244..400b9c8 100644 --- a/jac-gpt/server/docs/assets/examples/basic/assignments.jac +++ b/jac-gpt/server/docs/assets/examples/basic/assignments.jac @@ -1,6 +1,6 @@ with entry { a = b=16; - let c = 18; + c = 18; print(a, b, c); a >>= 2; print(a); diff --git a/jac-gpt/server/docs/communityhub/breaking_changes.md b/jac-gpt/server/docs/communityhub/breaking_changes.md index 154fe8e..d19d6fd 100644 --- a/jac-gpt/server/docs/communityhub/breaking_changes.md +++ b/jac-gpt/server/docs/communityhub/breaking_changes.md @@ -6,6 +6,37 @@ This page documents significant breaking changes in Jac and Jaseci that may affe MTLLM library is now deprecated and replaced by the byLLM package. In all place where `mtllm` was used before can be replaced with `byllm`. +### Version 0.9.4 + +#### 1. `let` Keyword Removed - Use Direct Assignment + +The `let` keyword has been removed from Jaclang. Variable declarations now use direct assignment syntax, aligning with Python's approach to variable binding. + +**Before** + +```jac +with entry { + let x = 10; + let name = "Alice"; + let [count, setCount] = useState(0); +} +``` + +**After** + +```jac +with entry { + x = 10; + name = "Alice"; + [count, setCount] = useState(0); +} +``` + +**Key Changes:** +- Remove the `let` keyword from all variable declarations +- Use direct assignment (`x = value`) instead of `let x = value` +- This applies to all contexts including destructuring assignments + ### Version 0.8.10 #### 1. byLLM Imports Moved to `byllm.lib` diff --git a/jac-gpt/server/docs/communityhub/release_notes/jaclang.md b/jac-gpt/server/docs/communityhub/release_notes/jaclang.md index 39a29b7..7debf7b 100644 --- a/jac-gpt/server/docs/communityhub/release_notes/jaclang.md +++ b/jac-gpt/server/docs/communityhub/release_notes/jaclang.md @@ -4,8 +4,10 @@ This document provides a summary of new features, improvements, and bug fixes in ## jaclang 0.9.4 (Unreleased) +- **`let` Keyword Removed**: The `let` keyword has been removed from Jaclang. Variable declarations now use direct assignment syntax (e.g., `x = 10` instead of `let x = 10`), aligning with Python's approach to variable binding. +- **Py2Jac Robustness Improvements**: Improved reliability of Python-to-Jac conversion with better handling of f-strings (smart quote switching, no keyword escaping in interpolations), match pattern class names, attribute access formatting (no extra spaces around dots), and nested docstrings in classes and functions. - **Format Command Enhancements**: The `jac format` command now tracks and reports which files were actually changed during formatting. The summary output shows both total files processed and the count of files that were modified (e.g., `Formatted 10/12 '.jac' files (3 changed).`). Additionally, syntax errors encountered during formatting are now printed with full error details. -- **Py2Jac Stability**: Fixed conversion of Python code with augmented assignments and nested docstrings so generated Jac no longer redeclares targets or merges docstrings into following defs. +- **F-String Escape Sequence Fix**: Fixed a bug where escape sequences like `\n`, `\t`, etc. inside f-strings were not being properly decoded, causing literal backslash-n to appear in output instead of actual newlines. The fix correctly decodes escape sequences for f-string literal fragments in `unitree.py`. ## jaclang 0.9.3 (Latest Release) diff --git a/jac-gpt/server/docs/learn/imports/basics.md b/jac-gpt/server/docs/learn/imports/basics.md new file mode 100644 index 0000000..f50bf60 --- /dev/null +++ b/jac-gpt/server/docs/learn/imports/basics.md @@ -0,0 +1,182 @@ +# Import Basics + +Jac provides a powerful and flexible import system to organize code across multiple files and packages. + +!!! tip "Prefer Absolute Imports" + **We recommend using absolute imports** over relative imports. Absolute imports are explicit, easier to read, and avoid ambiguity. + +--- + +## Import Syntax Overview + +| Pattern | Syntax | Use Case | +|---------|--------|----------| +| Absolute import | `import module;` | Import entire module | +| From-import | `import from module { X, Y }` | Import specific symbols | +| Include (wildcard) | `include module;` | Include all symbols into namespace | +| Aliased import | `import module as alias;` | Rename module | +| From-import alias | `import from module { X as Y }` | Rename symbol | + +!!! note "File Extensions" + Jac resolves both `.jac` and `.py` filesβ€”you don't need to include the extension in import paths. This makes Jac fully interoperable with Python modules. + +--- + +## Absolute Import + +Import an entire module and access its members using dot notation. + +> πŸ“‚ [**absolute_import/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/absolute_import) + +=== "main.jac" + ```jac title="absolute_import/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/absolute_import/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/absolute_import/main.jac) + +=== "module_a.jac" + ```jac title="absolute_import/module_a.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/absolute_import/module_a.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/absolute_import/module_a.jac) + +``` +absolute_import/ +β”œβ”€β”€ main.jac +└── module_a.jac +``` + +??? example "Output" + ``` + Absolute import - VALUE_A: Hello from module_a + Absolute import - greet(): Greet from module_a + ``` + +!!! tip "When to use" + Use absolute imports when you need multiple items from a module and want to make the source clear (e.g., `module_a.VALUE_A`). + +--- + +## From-Import (Selective Import) + +Import specific symbols directly into your namespace. + +> πŸ“‚ [**from_import/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/from_import) + +=== "main.jac" + ```jac title="from_import/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/from_import/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/from_import/main.jac) + +=== "module_b.jac" + ```jac title="from_import/module_b.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/from_import/module_b.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/from_import/module_b.jac) + +``` +from_import/ +β”œβ”€β”€ main.jac +└── module_b.jac +``` + +??? example "Output" + ``` + From-import - VALUE_B: Hello from module_b + From-import - calculate(5): 10 + From-import - MyClass: MyClass instance + ``` + +!!! tip "When to use" + Use from-imports when you need specific items and want shorter names in your code. + +--- + +## Include Statement (Wildcard Import) + +The `include` statement imports all public symbols from a module directly into your namespace. + +> πŸ“‚ [**include_statement/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/include_statement) + +=== "main.jac" + ```jac title="include_statement/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/include_statement/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/include_statement/main.jac) + +=== "module_c.jac" + ```jac title="include_statement/module_c.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/include_statement/module_c.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/include_statement/module_c.jac) + +``` +include_statement/ +β”œβ”€β”€ main.jac +└── module_c.jac +``` + +??? example "Output" + ``` + Star import - PUBLIC_VAR: I am public + Star import - public_func(): Public function + ``` + +!!! info "Private symbols" + Symbols starting with `_` (underscore) are considered private and are **not** included. + +!!! warning "Use sparingly" + Include statements can pollute your namespace. Prefer explicit imports in production code. + +--- + +## Aliased Imports + +Rename modules or symbols during import to avoid conflicts or for convenience. + +> πŸ“‚ [**aliased_import/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/aliased_import) + +=== "main.jac" + ```jac title="aliased_import/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/aliased_import/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/aliased_import/main.jac) + +=== "module_d.jac" + ```jac title="aliased_import/module_d.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/aliased_import/module_d.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/aliased_import/module_d.jac) + +``` +aliased_import/ +β”œβ”€β”€ main.jac +└── module_d.jac +``` + +??? example "Output" + ``` + Import as - md.LONG_MODULE_VALUE: Value from long named module + From import as - lfn(): Result from long function + ``` + +!!! tip "When to use" + - Shorten long module names + - Avoid naming conflicts + - Create more descriptive names + +--- + +## Key Takeaways + +| Concept | Description | +|---------|-------------| +| **`import X;`** | Access via `X.symbol` | +| **`import from X { Y }`** | Access `Y` directly | +| **`include X;`** | All public symbols available directly | +| **`import X as Z;`** | Access via `Z.symbol` | +| **`import from X { Y as Z }`** | Access `Y` as `Z` | + +!!! success "Best Practice: Use Absolute Imports" + Absolute imports like `import from mypackage.module { X }` are clearer and more maintainable than relative imports. diff --git a/jac-gpt/server/docs/learn/imports/packages.md b/jac-gpt/server/docs/learn/imports/packages.md new file mode 100644 index 0000000..110e382 --- /dev/null +++ b/jac-gpt/server/docs/learn/imports/packages.md @@ -0,0 +1,273 @@ +# Packages + +Packages in Jac are directories containing `.jac` or `.py` files. + +!!! tip "Prefer Absolute Imports" + Use absolute imports like `import from mypackage.module { X }` for clarity and maintainability. + +--- + +## Simple Package (No `__init__.jac` Required) + +Import directly from a package directory. The `__init__.jac` file is **optional**. + +> πŸ“‚ [**package_no_init/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init) + +=== "main.jac" + ```jac title="package_no_init/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init/main.jac) + +=== "mylib/math_utils.jac" + ```jac title="package_no_init/mylib/math_utils.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init/mylib/math_utils.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init/mylib/math_utils.jac) + +=== "mylib/string_utils.jac" + ```jac title="package_no_init/mylib/string_utils.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init/mylib/string_utils.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_no_init/mylib/string_utils.jac) + +``` +package_no_init/ +β”œβ”€β”€ main.jac +└── mylib/ # No __init__.jac needed! + β”œβ”€β”€ math_utils.jac + └── string_utils.jac +``` + +??? example "Output" + ``` + add(3, 5): 8 + multiply(4, 6): 24 + greet('World'): Hello, World! + ``` + +--- + +## Package with Re-exports + +Use `__init__.jac` when you want to create a simplified public API. + +> πŸ“‚ [**package_basic/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic) + +=== "main.jac" + ```jac title="package_basic/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic/main.jac) + +=== "mypackage/__init__.jac" + ```jac title="package_basic/mypackage/__init__.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic/mypackage/__init__.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic/mypackage/__init__.jac) + +=== "mypackage/helper.jac" + ```jac title="package_basic/mypackage/helper.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic/mypackage/helper.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/package_basic/mypackage/helper.jac) + +``` +package_basic/ +β”œβ”€β”€ main.jac +└── mypackage/ + β”œβ”€β”€ __init__.jac # Re-exports from helper.jac + └── helper.jac +``` + +??? example "Output" + ``` + Package from-import - HELPER_VALUE: Helper value from package + Package from-import - helper_func(): Helper function result + ``` + +--- + +## Nested Package Imports + +Access deeply nested packages using dot notation. + +> πŸ“‚ [**nested_packages/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages) + +=== "main.jac" + ```jac title="nested_packages/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/main.jac) + +=== "app/__init__.jac" + ```jac title="nested_packages/app/__init__.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/__init__.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/__init__.jac) + +=== "app/constants.jac" + ```jac title="nested_packages/app/constants.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/constants.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/constants.jac) + +=== "app/models/user.jac" + ```jac title="nested_packages/app/models/user.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/models/user.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/models/user.jac) + +=== "app/services/user_service.jac" + ```jac title="nested_packages/app/services/user_service.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/services/user_service.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/nested_packages/app/services/user_service.jac) + +``` +nested_packages/ +β”œβ”€β”€ main.jac +└── app/ + β”œβ”€β”€ __init__.jac + β”œβ”€β”€ constants.jac + β”œβ”€β”€ models/ + β”‚ β”œβ”€β”€ __init__.jac + β”‚ └── user.jac + └── services/ + β”œβ”€β”€ __init__.jac + └── user_service.jac +``` + +??? example "Output" + ``` + Nested deep - APP_NAME: MyApp + Nested deep - get_version(): 1.0.0 + Nested deep - create_user('Alice'): User(Alice) from MyApp + ``` + +--- + +## Re-exports with `__init__.jac` + +While optional, `__init__.jac` can be used to create a clean public API by re-exporting symbols. + +> πŸ“‚ [**init_reexport/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport) + +=== "main.jac" + ```jac title="init_reexport/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/main.jac) + +=== "mathlib/__init__.jac" + ```jac title="init_reexport/mathlib/__init__.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/__init__.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/__init__.jac) + +=== "mathlib/operations.jac" + ```jac title="init_reexport/mathlib/operations.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/operations.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/operations.jac) + +=== "mathlib/constants.jac" + ```jac title="init_reexport/mathlib/constants.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/constants.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/constants.jac) + +=== "mathlib/calculator.jac" + ```jac title="init_reexport/mathlib/calculator.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/calculator.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/init_reexport/mathlib/calculator.jac) + +``` +init_reexport/ +β”œβ”€β”€ main.jac +└── mathlib/ + β”œβ”€β”€ __init__.jac # Re-exports from submodules + β”œβ”€β”€ calculator.jac + β”œβ”€β”€ constants.jac + └── operations.jac +``` + +??? example "Output" + ``` + Init reexport - add(5, 3): 8 + Init reexport - subtract(10, 4): 6 + Init reexport - multiply(6, 7): 42 + Init reexport - divide(20, 4): 5.0 + Init reexport - PI: 3.14159 + Init reexport - E: 2.71828 + Init reexport - Calculator.compute(10, 2, 'add'): 12 + ``` + +!!! tip "When to use `__init__.jac`" + Use `__init__.jac` when you want to: + + - Create a simplified public API + - Hide internal module structure + - Add package-level constants or initialization + +--- + +## Sibling Subpackage Imports + +Import between sibling subpackages using absolute paths. + +> πŸ“‚ [**sibling_subpackage/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage) + +=== "main.jac" + ```jac title="sibling_subpackage/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage/main.jac) + +=== "top/sub_a/a_module.jac" + ```jac title="sibling_subpackage/top/sub_a/a_module.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage/top/sub_a/a_module.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage/top/sub_a/a_module.jac) + +=== "top/sub_b/b_module.jac" + ```jac title="sibling_subpackage/top/sub_b/b_module.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage/top/sub_b/b_module.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/sibling_subpackage/top/sub_b/b_module.jac) + +``` +sibling_subpackage/ +β”œβ”€β”€ main.jac +└── top/ + β”œβ”€β”€ __init__.jac + β”œβ”€β”€ sub_a/ + β”‚ β”œβ”€β”€ __init__.jac + β”‚ └── a_module.jac + └── sub_b/ + β”œβ”€β”€ __init__.jac + └── b_module.jac # Can import from sub_a +``` + +??? example "Output" + ``` + Sibling subpkg - A_VALUE: A module value + Sibling subpkg - a_func(): A function + Sibling subpkg - B_VALUE: B uses A module value + Sibling subpkg - b_func(): B calls: A function + ``` + +--- + +## Key Takeaways + +| Concept | Description | +|---------|-------------| +| **Packages** | Directories with `.jac` or `.py` files | +| **`__init__.jac`** | Optional, useful for re-exports | +| **Absolute imports** | `import from pkg.subpkg.module { X }` | +| **Nested access** | Use dot notation for deep packages | + +!!! success "Best Practice" + Use **absolute imports** with full package paths: `import from app.models.user { User }`. This is explicit and avoids ambiguity. diff --git a/jac-gpt/server/docs/learn/imports/relative_imports.md b/jac-gpt/server/docs/learn/imports/relative_imports.md new file mode 100644 index 0000000..458a59e --- /dev/null +++ b/jac-gpt/server/docs/learn/imports/relative_imports.md @@ -0,0 +1,151 @@ +# Relative Imports + +Relative imports allow modules within a package to reference each other using `.` (current) and `..` (parent) notation. + +!!! warning "Prefer Absolute Imports" + **Relative imports can be ambiguous.** We recommend absolute imports in most cases. Use relative imports only for tightly coupled internal package code. + +--- + +## Relative Import Syntax + +| Syntax | Meaning | +|--------|---------| +| `.module` | Same directory | +| `..module` | Parent directory | +| `...module` | Grandparent directory | + +--- + +## Same-Level Imports (`.`) + +Import from modules in the same directory. + +> πŸ“‚ [**relative_sibling/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling) + +=== "main.jac" + ```jac title="relative_sibling/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling/main.jac) + +=== "pkg/base.jac" + ```jac title="relative_sibling/pkg/base.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling/pkg/base.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling/pkg/base.jac) + +=== "pkg/sibling.jac" + ```jac title="relative_sibling/pkg/sibling.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling/pkg/sibling.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_sibling/pkg/sibling.jac) + +``` +relative_sibling/ +β”œβ”€β”€ main.jac +└── pkg/ + β”œβ”€β”€ base.jac # Defines BASE_VALUE, base_func + └── sibling.jac # Uses .base to import +``` + +??? example "Output" + ``` + Relative import - BASE_VALUE: Base value + Relative import - SIBLING_VALUE: Sibling uses Base value + Relative import - sibling_func(): Sibling calls: Base function + ``` + +--- + +## Parent-Level Imports (`..`) + +Import from the parent directory. + +> πŸ“‚ [**relative_parent/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent) + +=== "main.jac" + ```jac title="relative_parent/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent/main.jac) + +=== "project/config.jac" + ```jac title="relative_parent/project/config.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent/project/config.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent/project/config.jac) + +=== "project/sub/deep.jac" + ```jac title="relative_parent/project/sub/deep.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent/project/sub/deep.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/relative_parent/project/sub/deep.jac) + +``` +relative_parent/ +β”œβ”€β”€ main.jac +└── project/ + β”œβ”€β”€ config.jac # Defines CONFIG_VALUE, DEBUG + └── sub/ + └── deep.jac # Uses ..config to reach parent +``` + +??? example "Output" + ``` + Parent relative - CONFIG_VALUE: Project config + Parent relative - DEEP_VALUE: Deep module using config: Project config + Parent relative - deep_func(): Deep function, DEBUG=True + ``` + +--- + +## Mixed Absolute and Relative + +You can combine both import styles, but prefer absolute imports for clarity. + +> πŸ“‚ [**mixed_imports/**](https://github.com/Jaseci-Labs/jaseci/tree/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports) + +=== "main.jac" + ```jac title="mixed_imports/main.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports/main.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports/main.jac) + +=== "library/base.jac" + ```jac title="mixed_imports/library/base.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports/library/base.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports/library/base.jac) + +=== "library/extended.jac" + ```jac title="mixed_imports/library/extended.jac" + --8<-- "jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports/library/extended.jac" + ``` + [πŸ”— View on GitHub](https://github.com/Jaseci-Labs/jaseci/blob/main/jac/jaclang/compiler/tests/fixtures/imports_fixture/mixed_imports/library/extended.jac) + +``` +mixed_imports/ +β”œβ”€β”€ main.jac +└── library/ + β”œβ”€β”€ base.jac + └── extended.jac +``` + +??? example "Output" + ``` + Mixed - BASE_ID (from import): 1000 + Mixed - EXTENDED_ID: 1001 + Mixed - get_extended_id(): Base: 1000, Extended: 1001 + Mixed - lib_base.BASE_ID (alias): 1000 + ``` + +--- + +--- + +!!! warning "Relative Import Boundaries" + Relative imports only work within packages. You cannot use `..` to escape beyond your project's root. + +!!! success "Best Practice" + **Default to absolute imports.** They're explicit and don't break when you reorganize code. diff --git a/jac-gpt/server/test_datasets/test_dataset.json b/jac-gpt/server/test_datasets/test_dataset.json new file mode 100644 index 0000000..2b19113 --- /dev/null +++ b/jac-gpt/server/test_datasets/test_dataset.json @@ -0,0 +1,79 @@ +{ + "test_cases": [ + { + "id": "test_001", + "query": "How do I create a walker in Jac?", + "type": "coding" + }, + { + "id": "test_002", + "query": "What is the difference between a walker and a node?", + "type": "QA" + }, + { + "id": "test_003", + "query": "Show me an example of ability definition in Jac", + "type": "coding" + }, + { + "id": "test_004", + "query": "What are the new features in the latest Jac release?", + "type": "QA" + }, + { + "id": "test_005", + "query": "How does the by llm syntax work?", + "type": "coding" + }, + { + "id": "test_006", + "query": "What is Jaseci?", + "type": "QA" + }, + { + "id": "test_007", + "query": "Can you help me with Python?", + "type": "QA" + }, + { + "id": "test_008", + "query": "What's the weather like today?", + "type": "QA" + }, + { + "id": "test_009", + "query": "How do I use the visit keyword in Jac?", + "type": "coding" + }, + { + "id": "test_010", + "query": "Explain the import syntax in Jac", + "type": "coding" + }, + { + "id": "test_011", + "query": "What are the benefits of using Jac over Python?", + "type": "QA" + }, + { + "id": "test_012", + "query": "How do I define an enum in Jac?", + "type": "coding" + }, + { + "id": "test_013", + "query": "Tell me a joke", + "type": "QA" + }, + { + "id": "test_014", + "query": "What is the purpose of the has keyword?", + "type": "QA" + }, + { + "id": "test_015", + "query": "How do I install Jaseci?", + "type": "QA" + } + ] +} diff --git a/jac-gpt/server/test_evaluator.jac b/jac-gpt/server/test_evaluator.jac new file mode 100644 index 0000000..d872a80 --- /dev/null +++ b/jac-gpt/server/test_evaluator.jac @@ -0,0 +1,238 @@ +import sys; +import os; +import json; +import time; +import from datetime {datetime} +import from dotenv {load_dotenv} +import from server {infer} + + +"""Load test dataset from JSON file""" +def load_test_dataset(file_path: str) -> dict { + try { + with open(file_path, "r", encoding="utf-8") as file { + data = json.load(file); + return data; + } + } except Exception as e { + print(f"Error loading test dataset: {e}"); + return {"test_cases": []}; + } +} + +"""Save results to JSON file in logs folder""" +def save_results(results: dict, output_dir: str = ".logs") -> str { + try { + # Create logs directory if it doesn't exist + os.makedirs(output_dir, exist_ok=True); + + # Generate timestamp for filename + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S"); + filename = f"eval_results_{timestamp}.json"; + filepath = os.path.join(output_dir, filename); + + # Save results + with open(filepath, "w", encoding="utf-8") as file { + json.dump(results, file, indent=2, ensure_ascii=False); + } + + return filepath; + } except Exception as e { + print(f"Error saving results: {e}"); + return ""; + } +} + +"""Calculate summary statistics from test results""" +def calculate_summary(results: list[dict]) -> dict { + if not results { + return { + "total_tests": 0, + "min_latency_ms": 0.0, + "max_latency_ms": 0.0 + }; + } + + successful = [r for r in results if r.get("success", False)]; + + total_latencies = [r["latency_ms"] for r in successful if "latency_ms" in r]; + + # Calculate min/max safely + min_lat = 0.0; + max_lat = 0.0; + if total_latencies { + min_lat = total_latencies[0]; + max_lat = total_latencies[0]; + for lat in total_latencies { + if lat < min_lat { + min_lat = lat; + } + if lat > max_lat { + max_lat = lat; + } + } + } + + return { + "total_tests": len(results), + "min_latency_ms": min_lat, + "max_latency_ms": max_lat + }; +} + +"""Evaluate a test dataset and generate results with latency metrics""" +def evaluate_test_dataset( + test_file: str = "test_dataset.json", + output_dir: str = ".logs", + verbose: bool = True, + notes: str = "" +) { + + + print(f"\n{'='*60}"); + print(f"Starting Test Evaluation"); + print(f"{'='*60}"); + print(f"Test file: {test_file}"); + print(f"Output directory: {output_dir}\n"); + + # Load test dataset + dataset = load_test_dataset(test_file); + test_cases = dataset.get("test_cases", []); + + if not test_cases { + print("No test cases found in dataset!"); + return; + } + + print(f"Loaded {len(test_cases)} test cases\n"); + + # Run tests + results = []; + for idx in range(len(test_cases)) { + test_case = test_cases[idx]; + test_id = test_case.get("id", f"test_{idx+1}"); + query = test_case.get("query", ""); + + if verbose { + print(f"\n[{idx+1}/{len(test_cases)}] Running: {test_id}"); + print(f"Query: {query[:80]}{'...' if len(query) > 80 else ''}"); + } + + # Measure total latency + start_total = time.perf_counter(); + + try { + # Spawn infer walker from server.jac + response_walker = infer(message=query, chat_history=[]) spawn root; + + end_total = time.perf_counter(); + total_latency_ms = (end_total - start_total) * 1000; + + # Extract response from walker + response = response_walker.response; + + result = { + "test_id": test_id, + "query": query, + "response": response, + "response_length": len(response), + "latency_ms": round(total_latency_ms, 2), + "success": True, + "error": None + }; + + if verbose { + print(f"βœ“ Latency: {round(total_latency_ms, 2)}ms"); + } + + } except Exception as e { + end_total = time.perf_counter(); + total_latency_ms = (end_total - start_total) * 1000; + + result = { + "test_id": test_id, + "query": query, + "response": None, + "response_length": 0, + "latency_ms": round(total_latency_ms, 2), + "success": False, + "error": str(e) + }; + + if verbose { + print(f"βœ— Error: {str(e)}"); + } + } + + results.append(result); + } + + # Calculate summary + summary = calculate_summary(results); + + # Prepare final output + output = { + "test_run_metadata": { + "timestamp": datetime.now().isoformat(), + "test_file": test_file, + "total_tests": len(test_cases), + "model": "gpt-4.1-mini", + "notes": notes if notes else "No notes provided" + }, + "results": results, + "summary": summary + }; + + # Save to file + output_path = save_results(output, output_dir); + + # Print summary + print(f"\n{'='*60}"); + print(f"Evaluation Complete"); + print(f"{'='*60}"); + print(f"Total Tests: {summary['total_tests']}"); + print(f"Min Latency: {summary['min_latency_ms']:.2f}ms"); + print(f"Max Latency: {summary['max_latency_ms']:.2f}ms"); + print(f"\nResults saved to: {output_path}"); + print(f"{'='*60}\n"); +} + +with entry { + load_dotenv(override=True); + + print("\n=== Test Evaluator ==="); + print("1. Run test dataset"); + print("2. Test custom query"); + choice = input("Choose option (1 or 2): ").strip(); + + if choice == "1" { + notes = input("\nEnter notes for this test run (optional): ").strip(); + test_file_path = "test_datasets/test_dataset.json"; + evaluate_test_dataset(test_file=test_file_path, verbose=False, notes=notes); + } elif choice == "2" { + import from server {infer}; + query = input("\nEnter your query: ").strip(); + + if query { + print(f"\nProcessing: {query}"); + start = time.perf_counter(); + + response_walker = infer(message=query, chat_history=[]) spawn root; + + end = time.perf_counter(); + latency = (end - start) * 1000; + + print(f"\n{'='*60}"); + print(f"Response:"); + print(f"{'='*60}"); + print(response_walker.response); + print(f"\n{'='*60}"); + print(f"Latency: {latency:.2f}ms"); + print(f"{'='*60}\n"); + } else { + print("No query provided."); + } + } else { + print("Invalid choice. Exiting."); + } +}