From b8d183eb45b986e75199679f788328cae4919982 Mon Sep 17 00:00:00 2001 From: Poorvith M P Date: Mon, 13 Jul 2026 15:06:25 +0530 Subject: [PATCH] feat: add --label-language flag for LLM community labels (#1824) --- graphify/cli.py | 9 +++++++-- graphify/llm.py | 20 +++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/graphify/cli.py b/graphify/cli.py index 25bf5499b..4aed8bfac 100644 --- a/graphify/cli.py +++ b/graphify/cli.py @@ -951,6 +951,7 @@ def dispatch_command(cmd: str) -> None: co_exclude_hubs: float | None = None label_max_concurrency: int = 4 label_batch_size: int = 100 + label_language: str | None = None i_arg = 0 while i_arg < len(args): a = args[i_arg] @@ -980,6 +981,10 @@ def dispatch_command(cmd: str) -> None: label_batch_size = int(args[i_arg + 1]); i_arg += 2 elif a.startswith("--batch-size="): label_batch_size = int(a.split("=", 1)[1]); i_arg += 1 + elif a == "--label-language" and i_arg + 1 < len(args): + label_language = args[i_arg + 1]; i_arg += 2 + elif a.startswith("--label-language="): + label_language = a.split("=", 1)[1]; i_arg += 1 elif a in ("--no-viz", "--missing-only") or a.startswith("--min-community-size="): i_arg += 1 elif a.startswith("--"): @@ -1165,7 +1170,7 @@ def dispatch_command(cmd: str) -> None: generated_labels, _ = generate_community_labels( G, label_communities_input, backend=label_backend, model=label_model, gods=gods, max_concurrency=label_max_concurrency, batch_size=label_batch_size, - usage_out=label_token_usage, + usage_out=label_token_usage, label_language=label_language, ) # Only let the LLM OVERRIDE where it produced a real name — its no-backend # fallback returns "Community {cid}" placeholders, which must not clobber @@ -2769,4 +2774,4 @@ def _progress(idx: int, total: int, _result: dict) -> None: else: print(f"error: unknown command '{cmd}'", file=sys.stderr) print("Run 'graphify --help' for usage.", file=sys.stderr) - sys.exit(1) + sys.exit(1) \ No newline at end of file diff --git a/graphify/llm.py b/graphify/llm.py index 560bbcb88..f86781b19 100644 --- a/graphify/llm.py +++ b/graphify/llm.py @@ -2373,6 +2373,7 @@ def _label_batch_with_retry( depth: int = 0, max_depth: int = 3, usage_out: dict | None = None, + label_language: str | None = None, ) -> dict[int, str]: """Label a batch of communities, splitting in half and retrying on parse failure. @@ -2389,11 +2390,14 @@ def _label_batch_with_retry( missing config, programming bug) propagates unchanged — those are never split-retried. """ + _lang = label_language or os.environ.get("GRAPHIFY_LABEL_LANGUAGE", "") + _lang_clause = f" Respond in {_lang}." if _lang else "" prompt = ( "You are naming clusters in a knowledge graph. For each community below, " "return a concise 2-5 word plain-language name describing what it is about " - "(e.g. \"Order Management\", \"Payment Flow\", \"Auth Middleware\"). " - "Respond ONLY with a JSON object mapping the community id (as a string) to " + "(e.g. \"Order Management\", \"Payment Flow\", \"Auth Middleware\")." + + _lang_clause + + " Respond ONLY with a JSON object mapping the community id (as a string) to " "its name - no prose, no markdown fences.\n\n" + "\n".join(batch_lines) ) # Budget generously: a 2-5 word name is ~10 tokens, but models (notably @@ -2428,12 +2432,12 @@ def _label_batch_with_retry( left = _label_batch_with_retry( batch_cids[:mid], batch_lines[:mid], backend=backend, model=model, depth=depth + 1, max_depth=max_depth, - usage_out=usage_out, + usage_out=usage_out, label_language=label_language, ) right = _label_batch_with_retry( batch_cids[mid:], batch_lines[mid:], backend=backend, model=model, depth=depth + 1, max_depth=max_depth, - usage_out=usage_out, + usage_out=usage_out, label_language=label_language, ) return left | right @@ -2450,6 +2454,7 @@ def label_communities( batch_size: int = _LABEL_BATCH_SIZE, max_concurrency: int = 4, usage_out: dict | None = None, + label_language: str | None = None, ) -> dict[int, str]: """Return a complete ``{cid: name}`` map using ``backend`` for naming. @@ -2500,7 +2505,7 @@ def _run_batch(batch_idx: int): try: parsed = _label_batch_with_retry( labeled_cids[start:end], lines[start:end], backend=backend, model=model, - **batch_kwargs, + label_language=label_language, **batch_kwargs, ) return batch_idx, parsed, None, batch_usage except Exception as exc: # noqa: BLE001 - reported per-batch; surfaced below @@ -2558,6 +2563,7 @@ def generate_community_labels( max_concurrency: int = 4, batch_size: int = _LABEL_BATCH_SIZE, usage_out: dict | None = None, + label_language: str | None = None, ) -> tuple[dict[int, str], str]: """CLI entry point: resolve a backend, name communities, and degrade to ``Community N`` placeholders on any failure (no backend, API error, malformed @@ -2580,7 +2586,7 @@ def generate_community_labels( labels = label_communities( G, communities, backend=backend, model=model, gods=gods, max_concurrency=max_concurrency, batch_size=batch_size, - usage_out=usage_out, + usage_out=usage_out, label_language=label_language, ) return labels, "llm" except Exception as exc: @@ -2590,4 +2596,4 @@ def generate_community_labels( "using Community N placeholders.", file=sys.stderr, ) - return _placeholder_community_labels(communities), "placeholder" + return _placeholder_community_labels(communities), "placeholder" \ No newline at end of file