fix code display for temporary modules
diff --git a/hamilton/ad_hoc_utils.py b/hamilton/ad_hoc_utils.py
index 773d078..5b2dab3 100644
--- a/hamilton/ad_hoc_utils.py
+++ b/hamilton/ad_hoc_utils.py
@@ -5,7 +5,7 @@
 import types
 import uuid
 from types import ModuleType
-from typing import Callable
+from typing import Callable, Optional
 
 
 def _copy_func(f):
@@ -60,9 +60,9 @@
     return module
 
 
-def module_from_source(source: str) -> ModuleType:
+def module_from_source(source: str, module_name: Optional[str] = None) -> ModuleType:
     """Create a temporary module from source code"""
-    module_name = _generate_unique_temp_module_name()
+    module_name = module_name if module_name else _generate_unique_temp_module_name()
     module_object = ModuleType(module_name)
     code_object = compile(source, module_name, "exec")
     sys.modules[module_name] = module_object
diff --git a/ui/sdk/src/hamilton_sdk/driver.py b/ui/sdk/src/hamilton_sdk/driver.py
index 0087af7..7677ce8 100644
--- a/ui/sdk/src/hamilton_sdk/driver.py
+++ b/ui/sdk/src/hamilton_sdk/driver.py
@@ -2,6 +2,7 @@
 import hashlib
 import inspect
 import json
+import linecache
 import logging
 import operator
 import os
@@ -67,15 +68,19 @@
                     f"attribute or it is None. This happens with lazy loaders."
                 )
                 continue
-            # Check if the module is in the same top level package
-            if value.__package__ != module.__package__ and not value.__package__.startswith(
-                module.__package__
-            ):
-                logger.debug(
-                    f"Skipping hash for module {value.__name__} because it is in a different "
-                    f"package {value.__package__} than {module.__package__}"
-                )
-                continue
+
+            # Modules imported in a temporary module have no `__package__` attribute
+            if module.__package__:
+                # Check if the module is in the same top level package
+                if value.__package__ != module.__package__ and not value.__package__.startswith(
+                    module.__package__
+                ):
+                    logger.debug(
+                        f"Skipping hash for module {value.__name__} because it is in a different "
+                        f"package {value.__package__} than {module.__package__}"
+                    )
+                    continue
+
             # Recursively hash the sub-module
             hash_object = _hash_module(value, hash_object, seen_modules)
 
@@ -688,6 +693,11 @@
 
 
 def _slurp_code(fg: graph.FunctionGraph, repo_base: str) -> List[dict]:
+    """Get the source code from modules. Returns a list with a dictionary for each module.
+
+    The `path` attribute needs to match the `path` of code artifacts generated by
+    `extract_code_artifacts_from_function_graph()`
+    """
     modules = set()
     for node_ in fg.nodes.values():
         originating_functions = node_.originating_functions
@@ -702,6 +712,14 @@
             module_path = os.path.relpath(module.__file__, repo_base)
             with open(module.__file__, "r") as f:
                 out.append({"path": module_path, "contents": f.read()})
+        # for temporary modules registed via `module_from_source`
+        else:
+            # get source code from the linecache; returns a tuple (size, mtime, lines, fullname)
+            source_lines = linecache.cache[module.__name__][2]
+            source = "".join(source_lines)
+            # the path won't have a `.py` suffix to match `extract_code_artifacts_from_function_grap()`
+            module_path = os.path.relpath(module.__name__, repo_base)
+            out.append({"path": module_path, "contents": source})
     return out