3 toolsOAuth bearer
gdocs_example.py
Authenticating
This pack takes a Google OAuth bearer token, andconfigure() is optional when $GOOGLE_ACCESS_TOKEN is set. Which credential provider you hand it depends on whose account the calls run as.
Refer to Google’s provider page for the “GOOGLE” constant the snippets below name, the scopes these 3 tools ask for, and this server’s refresh behaviour.
A token you hold
For a script, or a notebook.EnvTokenProvider re-reads the variable on every call, so a token rotated beside the process is picked up without a restart; StaticTokenProvider takes one you already hold as a string. Neither renews anything, so the calls stop when the token expires.
gdocs_script.py
One account, refreshed
For an agent or a server acting as you.OAuth2Client turns a client registration and a stored refresh token into an access token, and renews it before it lapses.
gdocs_agent.py
No refresh token yet? Your own account is the one-time consent flow that hands you one.
Many end users
For a product whose users each connect their own account.SubjectProvider builds one credential per user through a factory you write, and use_subject names the user a call acts for. Your users’ accounts is the consent route inside your app; serving many users is the per-subject cache and its eviction.
gdocs_server.py
documents.readonly is enough for documents_get alone, but the pack declares
the write scope because two of its three tools need it.
The client
oauth_tool_factory is the whole client: a thin wrapper over httpx that attaches your token and these endpoint constants to each request. google-api-python-client and google-auth do not enter your dependency tree.credentials is whichever of the three you built in Authenticating. A pack takes it through configure(); a client you build takes the same object as credential_provider, and has no configure() of its own.
Tools
Each is aTool, called with
ainvoke as in the snippet above. The name
links to its parameters, its response and what it costs.
documents_getGETRead a document’s full structural content.documents_createPOSTCreate a blank document with a title.documents_batch_updatePOSTApply a list of edits to a document.
batchUpdate is a union of thirty-three edits
Every change you can make to a document — insert text, style a range, merge table cells, pin header rows — is one member of aRequest oneof, and a batch is a
list of them. All thirty-three are modelled, with the oneof declared as a
model_validator on Request and on the eleven edits that carry a oneof of
their own.
gdocs_insert_text.py
extra="forbid" across — so a request setting two members of the union is
rejected against the model’s input, not only against the wire. Before that, the
one input that most needed checking was the one input never checked. See
tests/test_schema.py, and conformance for the
property that keeps it true.
Gotchas
Indices shift as edits apply — order the requests back-to-front
Indices shift as edits apply — order the requests back-to-front
batchUpdate applies its requests in order against a document that changes
under them. Insert text at index 1 and every later index moves; the API will
not warn you. When inserting at several positions, order the requests from
the highest index down. This is the single most common way a correct-looking
batch produces a scrambled document, and it is documented on the requests
field for the model’s benefit as well as yours.documents_batch_update is expensive in context
documents_batch_update is expensive in context
Its JSON schema is roughly 31KB — about 8,000 tokens — before the model has
read a word of the actual task. That is what complete coverage of a union
this wide costs.
Mode markers plus a tool-level mode narrow the union to
the edits one agent needs without giving up the schema; see
the mode system.Responses are not modelled
Responses are not modelled
A
Document is a deeply nested structural document, and Charter models
requests. documents_get returns Google’s payload as it arrived, with no
response handler. For long documents, write one that extracts the text — the
Gmail pack’s extract_thread_text is the shape to copy.The top-level body is only the first tab
The top-level body is only the first tab
A document may have several tabs, and
body reflects only the first. Pass
include_tabs_content=true to documents_get for the whole thing. The
argument is snake_case in the schema and reaches Google as
includeTabsContent, which is what the camel query casing on this factory is
for.documents_create honours only the title
documents_create honours only the title
The document is created empty whatever else you put in the body. Adding
content is a second call to
documents_batch_update with the returned
documentId.Related
- Google — consent screen, scopes, refresh
- The mode system — narrowing a union the model does not need
- Conformance — the check that keeps validators from being dropped