linexcel.aidoc¶
linexcel.aidoc ¶
AI-generated documentation for Excel calculations.
Vendor-neutral by construction: no provider is named in the code and none is chosen for you. There are exactly two ways in:
base_url=— any OpenAI-compatible endpoint (a local Ollama, vLLM or LM Studio runtime; a hosted gateway such as OpenRouter; OpenAI itself; anything else that speaks/chat/completions)provider=— your own callable or :class:LLMProviderobject, for an API that speaks something else entirely
The model doesn't guess: each node is presented with its deterministic dossier from the graph (exact formula, step-by-step evaluation, precedents and their values, dependents, stretched group extent, VBA links). The system prompt enforces citing only these facts, making the documentation "provable": every claim traces back to a formula or a workbook value.
TokenUsage
dataclass
¶
Tokens consumed by one or more documentation requests.
estimated is True as soon as any request in the tally had to be
approximated by :func:estimate_tokens instead of being reported by the
provider — treat such a total as an order of magnitude, not a bill.
Source code in src/linexcel/aidoc.py
add ¶
Accumulate other in place, keeping the model/provider labels.
Source code in src/linexcel/aidoc.py
LLMProvider ¶
Bases: Protocol
Minimal protocol: system + user prompt → text response.
Source code in src/linexcel/aidoc.py
UsageReportingProvider ¶
Bases: Protocol
A provider that also reports what the call consumed.
Optional: the built-in OpenAI-compatible client implements it so that token
counts come from the API rather than from an approximation. Custom
providers only need :class:LLMProvider.
Source code in src/linexcel/aidoc.py
estimate_tokens ¶
Approximate the token count of text.
Only a fallback: :class:TokenUsage prefers the counts the provider
reports. Latin script is counted as words × 4/3 (the usual 1 token ≈ 0.75
words ratio); CJK characters are counted individually, because a Japanese
or Chinese sentence carries no spaces and would otherwise register as a
single word.
estimate_tokens("the quick brown fox jumps") 6 estimate_tokens("") 0
Source code in src/linexcel/aidoc.py
build_dossier ¶
Deterministic dossier for a node: everything the AI is allowed to use.
Source code in src/linexcel/aidoc.py
build_workbook_dossier ¶
build_workbook_dossier(graph: dict[str, Any], *, context: dict[str, Any] | None = None) -> dict[str, Any]
Return a compact, deterministic dossier for a whole-workbook overview.
context is a :attr:linexcel.LineageResult.workbook_context mapping.
The graph alone describes how a workbook computes; it says nothing about
what a reader sees on opening it — titles sitting above a table, the labels
in the first column, cell comments, hidden columns, frozen panes. Those cues
are exactly what the sheet screenshots show, and merging them into each
sheet entry lets a text-only model describe the file as it looks without any
image ever leaving the machine.
Both parts stay deterministic: every value is read from the workbook, so the "cite only the dossier" rule of the system prompt still holds.
Source code in src/linexcel/aidoc.py
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | |
document_workbook ¶
document_workbook(graph: dict[str, Any], *, model: str | None = None, api_key: str | None = None, base_url: str | None = None, provider: ProviderLike | None = None, language: str = 'en', usage: TokenUsage | None = None, max_tokens: int | None = None, token_budget: int | None = None, context: dict[str, Any] | None = None) -> str
Generate a Markdown overview grounded in the workbook dossier.
Provider resolution (first match wins; no implicit default):
1. provider — custom LLMProvider instance or callable
2. base_url + model (or LINEXCEL_AI_BASE_URL + LINEXCEL_AI_MODEL) —
any OpenAI-compatible endpoint
context is the workbook presentation context — the sheet previews,
comments, merged cells, frozen panes and hidden columns a reader sees when
opening the file. Pass it to describe the workbook as it looks, not only as
it computes; see :func:build_workbook_dossier.
If a :class:TokenUsage is passed as usage, what the call consumed is
accumulated into it. token_budget caps cumulative spend across that
accumulator: an already-exhausted budget raises before anything is sent.
Source code in src/linexcel/aidoc.py
document_nodes ¶
document_nodes(graph: dict[str, Any], node_ids: list[str], *, model: str | None = None, api_key: str | None = None, base_url: str | None = None, provider: ProviderLike | None = None, language: str = 'en', max_workers: int = 4, usage: TokenUsage | None = None, max_tokens: int | None = None, token_budget: int | None = None) -> dict[str, str]
Document the requested nodes, returns {node_id: markdown}.
Provider resolution is the same as :func:document_workbook (no implicit
default; see :func:_resolve_provider).
Nodes are documented concurrently (max_workers in-flight requests;
raise it if the provider's rate limits allow). Documenting a large
workbook is a long, often billed operation, so a node that fails does not
discard the ones that succeeded: the successful cards are returned and a
:class:UserWarning reports how many nodes were dropped.
:class:AiDocError is raised only when every node failed.
If a :class:TokenUsage is passed as usage, every successful call is
accumulated into it — including those of a run that later fails, since
tokens already spent are still billed.
token_budget is a ceiling on the total tokens the run may spend,
input and output together, counted against usage so several calls
sharing one accumulator share one ceiling. It is enforced between requests,
the only point at which a cost is known: nodes still queued when the tally
reaches the budget are never sent, and a :class:UserWarning reports how
many were left undocumented. Requests already in flight are allowed to
finish, so the final tally can exceed the budget by up to max_workers
responses — set it as an order of magnitude, not to the token. Use
max_tokens to bound each individual response instead.
Source code in src/linexcel/aidoc.py
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 | |