Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
aae8fcb
Ready for long run
mateomorin Feb 19, 2026
5902128
Parallelization and user prompt fix
mateomorin Feb 19, 2026
b74e8cd
Better batch loop, adjusted system prompt to avoid hallucinations
mateomorin Feb 19, 2026
f889ff6
Ready for French NAF2025
mateomorin Feb 20, 2026
b9fd9a6
Added Few-shot
mateomorin Feb 20, 2026
2c02ab5
Fewshot modularity and specific system prompt
mateomorin Feb 23, 2026
62c221d
Comment fewshot functions
mateomorin Feb 23, 2026
7dd8006
Fast few-shot sampling for list of codes
mateomorin Feb 23, 2026
140a5cf
Exhaustivity for sampling
mateomorin Feb 26, 2026
1996779
Fixing bugs on exhaustivity, correcting some comments
mateomorin Feb 26, 2026
bb80b7b
get all codes multiple times, correct warning for neo4j calls, moved …
mateomorin Feb 26, 2026
f1a4dd8
Ready for exhaustive generation
mateomorin Feb 26, 2026
807eef2
New generation: changed generation path, netter name for config variable
mateomorin Mar 9, 2026
5fce3fc
Restructured NaiveCode2Text for better readibility
mateomorin Mar 9, 2026
c1fa4c0
Function for naming files, second trial for generation, and code ligh…
mateomorin Mar 9, 2026
d628c0d
Exhaustive spec, prompt creation restructured, alias for config varia…
mateomorin Mar 12, 2026
727ea1d
file name change, unique neo4jrequest for spec, and log progess
mateomorin Mar 12, 2026
c5bd806
docker image creation and script adapt
mateomorin Mar 13, 2026
c7e4995
Fixing bug: forgot value code in code_dict
mateomorin Mar 16, 2026
bb48617
Fixing bug: correct handling of code_details
mateomorin Mar 16, 2026
46c3c75
Added reproductibility folder
mateomorin Mar 16, 2026
7314b94
Hydra config
mateomorin Mar 16, 2026
c65403f
Hydra lib
mateomorin Mar 16, 2026
624e091
Hydra fix directory
mateomorin Mar 16, 2026
e2cdd53
Implemented Hydra with modular config
mateomorin Mar 19, 2026
5726029
Changed default output to terminal
mateomorin Mar 19, 2026
866de40
corrected typo in excludeS_divider
mateomorin Mar 19, 2026
6e668e5
Added Qwen config
mateomorin Mar 19, 2026
be36fc1
reduced generation batch size for qwen (agentic)
mateomorin Mar 19, 2026
3363428
Logger print only once
mateomorin Mar 19, 2026
353f6ce
Removed unused files
mateomorin May 12, 2026
e4101f5
Apply suggestions from code review
mateomorin May 13, 2026
1c7fc98
Changes requested by copilot for PR
mateomorin May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Personnal usage
test.ipynb
test.py

# Hydra
outputs/

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ requires-python = ">=3.13"
dependencies = [
"duckdb>=1.4.4",
"fastparquet>=2025.12.0",
"hydra-core>=1.3.2",
"ipykernel>=7.1.0",
"ipywidgets>=8.1.8",
"jupyter-cache>=1.0.1",
Expand Down
36 changes: 4 additions & 32 deletions src/agents/NaiveCode2Text/code_retrieval/code_sampler.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,13 @@
import polars as pl
import s3fs
import numpy as np


def sample_codes(
fs: s3fs.S3FileSystem,
population_path: str,
code_column: str,
n_codes: int
) -> np.ndarray:
"""
Sample codes with replacement using dataframes from Polars.

Args:
fs (S3FileSystem): The filesystem for importation.
population_path (str): The path of the parquet file of the population.
code_column (str): The name of the column for codes.
n_codes (int): The number of codes to sample.

Returns:
numpy.ndarray: An array of n_codes codes sampled with replacement.
"""

with fs.open(population_path, 'rb') as f:
df = pl.read_parquet(f)

sampled = df.select(code_column).sample(n=n_codes, with_replacement=True)

return sampled[code_column].to_numpy()


def sample_codes_lazy(
fs: s3fs.S3FileSystem,
population_path: str,
code_column: str,
n_codes: str
) -> np.ndarray:
n_codes: int
) -> list:
"""
Sample codes with replacement using lazyframes from Polars.

Expand All @@ -46,7 +18,7 @@ def sample_codes_lazy(
n_codes (int): The number of codes to sample.

Returns:
numpy.ndarray: An array of n_codes codes sampled with replacement.
list: A list of n_codes codes sampled with replacement.
"""

with fs.open(population_path, 'rb') as f:
Expand All @@ -68,4 +40,4 @@ def sample_codes_lazy(

df = sampled.collect()

return df[code_column].to_numpy()
return df[code_column].to_list()
132 changes: 109 additions & 23 deletions src/agents/NaiveCode2Text/code_retrieval/code_specifier.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
from src.neo4j_graph.graph import Graph
import logging

from langchain_neo4j import Neo4jGraph

logger = logging.getLogger(__name__)


def get_all_codes(
graph: Neo4jGraph,
n_samples_per_code: int
) -> list:
"""
Sample codes with replacement using lazyframes from Polars.

Args:
fs (S3FileSystem): The filesystem for importation.
population_path (str): The path of the parquet file of the population.
code_column (str): The name of the column for codes.
n_codes (int): The number of codes to sample.

Returns:
numpy.ndarray: An array of n_codes codes sampled with replacement.
"""

query = """
MATCH (n)
WHERE n.FINAL = 1
RETURN DISTINCT n.CODE;
"""

response = graph.query(query)

if not response:
logger.warn("No response in get_all_codes")
return []

result = []

for r in response:
result += [r["n.CODE"]] * n_samples_per_code

return result


def get_code_information(
graph: Graph,
graph: Neo4jGraph,
code: str
) -> dict:
"""
Expand All @@ -22,36 +63,81 @@ def get_code_information(
OPTIONAL MATCH (node)-[:HAS_CHILD]->(child)
WITH node, parent, collect({code: child.CODE, name: child.NAME}) as children
RETURN node.CODE as code,
node.LEVEL as level,
node.NAME as name,
node.text as description,
node.Includes as includes,
node.IncludesAlso as includes_also,
node.Excludes as excludes,
node.Implementation_rule as implementation_rule,
parent.CODE as parent_code,
children,
size(children) as children_count
"""
result = graph.graph.query(query, params={"code": code})
node.LEVEL as level,
node.NAME as name,
node.text as description,
node.Includes as includes,
node.IncludesAlso as includes_also,
node.Excludes as excludes,
node.Implementation_rule as implementation_rule,
parent.CODE as parent_code,
children,
size(children) as children_count
"""
result = graph.query(query, params={"code": code})

if not result:
print("No result in get_code_information")
return ()
logger.warn("No result in get_code_information")
return []

return result[0]


def NAF_to_NACE(
code: str
) -> str:
def get_code_list_information(
graph: Neo4jGraph,
code_list: list[str]
) -> dict:
"""
For the case of NAF code format (DDDDL), transform it into NACE (DD.DD).
Retrieve code specifications from a Neo4j graph for multiple codes.

Args:
code (str): The code in NAF format to transform.
graph (Neo4jGraph): The Neo4j graph.
code_list (list[str]): List of codes to retrieve.

Returns:
str: The code in NACE format.
dict: Dictionary of code information keyed by code.
"""
return code[0:2] + '.' + code[2:4]

if not code_list:
return {}

query = """
MATCH (node)
WHERE node.CODE IN $code_list
OPTIONAL MATCH (node)<-[:HAS_CHILD]-(parent)
OPTIONAL MATCH (node)-[:HAS_CHILD]->(child)
WITH node, parent, collect({code: child.CODE, name: child.NAME}) AS children
RETURN node.CODE AS code,
node.LEVEL AS level,
node.NAME AS name,
node.text AS description,
node.Includes AS includes,
node.IncludesAlso AS includes_also,
node.Excludes AS excludes,
node.Implementation_rule AS implementation_rule,
parent.CODE AS parent_code,
children,
size(children) AS children_count
"""

result = graph.query(query, params={"code_list": code_list})

if not result:
logger.warn("No result in get_code_list_information")
return {}

# Transformer la liste de résultats en dictionnaire de dictionnaires
code_dict = {}
for record in result:
code_key = record["code"]
code_dict[code_key] = {
"code": code_key,
"name": record["name"],
"description": record["description"],
"includes": record["includes"],
"includes_also": record["includes_also"],
"excludes": record["excludes"],
"implementation_rule": record["implementation_rule"]
}

return code_dict
34 changes: 0 additions & 34 deletions src/agents/NaiveCode2Text/config_naive.py

This file was deleted.

49 changes: 49 additions & 0 deletions src/agents/NaiveCode2Text/data_preprocessing/NAF_preprocessing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Useful if the code of the input data for sampling is different from the one
of the notice (DD.DDL).
"""


def NAF_to_NACE(
code: str
) -> str:
"""
For the case of NAF code format (DDDDL), transform it into NACE (DD.DD).

Args:
code (str): The code in NAF format to transform.

Returns:
str: The code in NACE format.
"""
return code[0:2] + '.' + code[2:4]


def to_proper_NAF(
code: str
) -> str:
"""
For the case of bad NAF code format (DDDDL), transform it into proper NAF (DD.DDL).

Args:
code (str): The code in bad NAF format to transform.

Returns:
str: The code in proper NAF format.
"""
return code[0:2] + '.' + code[2:]


def to_bad_NAF(
code: str
) -> str:
"""
For the case of proper NAF code format (DD.DDL), transform it into bad NAF (DDDDL).

Args:
code (str): The code in proper NAF format to transform.

Returns:
str: The code in bad NAF format.
"""
return code[0:2] + code[3:]
69 changes: 69 additions & 0 deletions src/agents/NaiveCode2Text/fewshot/fewshot_prompt_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging

logger = logging.getLogger(__name__)


def build_fewshot_system_prompt(
prompt_path: str,
language: str = "English",
nb_labels: int = 10,
) -> str:
"""
Import system prompt for fewshots with the correct language and specify the number of labels.

Args:
prompt_path (str): The path for importation.
language (str): English or French.
nb_labels (int): The number of labels to generate.

Returns:
str: The system prompt.
"""
if language == "French":
suffix = "_fr"
elif language == "English":
suffix = "_en"
else:
logger.warn("Supported languages are English and French. Switching to English...")
suffix = "_en"

# Importing prompt
file_path = prompt_path + "system_prompt_fewshot" + suffix + ".txt"
with open(file=file_path, mode='r') as f:
system_prompt = f.read()

# Indicating the correct number of labels
system_prompt = system_prompt.replace("{nb_labels}", str(nb_labels))
return system_prompt


def add_fewshot_user_prompt(
fewshot: list,
language: str = "English"
) -> str:
"""
Add few-shot to user prompt with the correct language.

Args:
fewshot (list): The list of exampls to give for few-shot.
language (str): English or French.

Returns:
str: The text to add to user prompt.
"""

if len(fewshot) == 0:
return ""

if language == "English":
prompt = "\nHere are examples of labels for the code:"
elif language == "French":
prompt = "\nVoici des exemples de libellés pour le code:"
else:
logger.warn("Supported languages are English and French. Switching to English...")
prompt = "\nHere are examples of labels for the code:"

for example in fewshot:
prompt += "\n- " + example

return prompt
Loading