autotouch-cli 0.2.47__tar.gz → 0.2.49__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/PKG-INFO +14 -5
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/README.md +13 -4
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/data/CLI_REFERENCE.md +1 -1
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/data/cli-manifest.json +1 -1
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli.egg-info/PKG-INFO +14 -5
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_shared/provider_registry.py +149 -20
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/pyproject.toml +1 -1
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/MANIFEST.in +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/__init__.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/cli.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/cli_contracts.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/__init__.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/auth.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/cells.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/columns.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/jobs.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/leads.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/linkedin.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/prompts.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/rows.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/search.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/sequences.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/tables.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/tasks.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/webhooks.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/commands/workspace_secrets.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/__init__.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/auth.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/config.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/http.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/io.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/output.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/core/polling.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/mongo_status.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/parser_groups.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/sequence_support.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli/templates.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli.egg-info/SOURCES.txt +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli.egg-info/dependency_links.txt +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli.egg-info/entry_points.txt +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli.egg-info/requires.txt +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_cli.egg-info/top_level.txt +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_shared/__init__.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_shared/linkedin_contract.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/autotouch_shared/search_contract.py +0 -0
- {autotouch_cli-0.2.47 → autotouch_cli-0.2.49}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autotouch-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.49
|
|
4
4
|
Summary: Autotouch Smart Table CLI
|
|
5
5
|
Project-URL: Homepage, https://app.autotouch.ai
|
|
6
6
|
Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
|
|
@@ -146,9 +146,18 @@ For automation or agent-driven setup, use:
|
|
|
146
146
|
- `autotouch sequences ...` and `autotouch tasks ...` for sequence/task workflows
|
|
147
147
|
- `pip install 'autotouch-cli[mongo]'` if you need the Mongo-backed `status` / `watch` commands
|
|
148
148
|
|
|
149
|
+
## LLM Columns
|
|
150
|
+
|
|
151
|
+
For `llm_enrichment` in `agent` mode, the recommended path is:
|
|
152
|
+
- provide `config.instructions`
|
|
153
|
+
- let the API compile the runnable prompt
|
|
154
|
+
- keep `config.useAutoSchema = true`
|
|
155
|
+
|
|
156
|
+
Only send `user_schema` / `response_schema` when you intentionally want to override the generated schema and keep it aligned yourself. The installed recipe surface at `autotouch columns recipe --type llm_enrichment` follows this contract.
|
|
157
|
+
|
|
149
158
|
## Docs
|
|
150
159
|
|
|
151
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
152
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
153
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
154
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
160
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/reference/autotouch-cli.md
|
|
161
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
162
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/reference/tables-api.md
|
|
163
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/platform/authentication.md
|
|
@@ -121,9 +121,18 @@ For automation or agent-driven setup, use:
|
|
|
121
121
|
- `autotouch sequences ...` and `autotouch tasks ...` for sequence/task workflows
|
|
122
122
|
- `pip install 'autotouch-cli[mongo]'` if you need the Mongo-backed `status` / `watch` commands
|
|
123
123
|
|
|
124
|
+
## LLM Columns
|
|
125
|
+
|
|
126
|
+
For `llm_enrichment` in `agent` mode, the recommended path is:
|
|
127
|
+
- provide `config.instructions`
|
|
128
|
+
- let the API compile the runnable prompt
|
|
129
|
+
- keep `config.useAutoSchema = true`
|
|
130
|
+
|
|
131
|
+
Only send `user_schema` / `response_schema` when you intentionally want to override the generated schema and keep it aligned yourself. The installed recipe surface at `autotouch columns recipe --type llm_enrichment` follows this contract.
|
|
132
|
+
|
|
124
133
|
## Docs
|
|
125
134
|
|
|
126
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
127
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
128
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
129
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
135
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/reference/autotouch-cli.md
|
|
136
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
137
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/reference/tables-api.md
|
|
138
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/platform/authentication.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autotouch-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.49
|
|
4
4
|
Summary: Autotouch Smart Table CLI
|
|
5
5
|
Project-URL: Homepage, https://app.autotouch.ai
|
|
6
6
|
Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
|
|
@@ -146,9 +146,18 @@ For automation or agent-driven setup, use:
|
|
|
146
146
|
- `autotouch sequences ...` and `autotouch tasks ...` for sequence/task workflows
|
|
147
147
|
- `pip install 'autotouch-cli[mongo]'` if you need the Mongo-backed `status` / `watch` commands
|
|
148
148
|
|
|
149
|
+
## LLM Columns
|
|
150
|
+
|
|
151
|
+
For `llm_enrichment` in `agent` mode, the recommended path is:
|
|
152
|
+
- provide `config.instructions`
|
|
153
|
+
- let the API compile the runnable prompt
|
|
154
|
+
- keep `config.useAutoSchema = true`
|
|
155
|
+
|
|
156
|
+
Only send `user_schema` / `response_schema` when you intentionally want to override the generated schema and keep it aligned yourself. The installed recipe surface at `autotouch columns recipe --type llm_enrichment` follows this contract.
|
|
157
|
+
|
|
149
158
|
## Docs
|
|
150
159
|
|
|
151
|
-
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
152
|
-
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
153
|
-
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
154
|
-
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.
|
|
160
|
+
- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/reference/autotouch-cli.md
|
|
161
|
+
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/guides/autotouch-cli-agent-playbook.md
|
|
162
|
+
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/research-table/reference/tables-api.md
|
|
163
|
+
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/autotouch-cli-v0.2.49/docs/platform/authentication.md
|
|
@@ -30,6 +30,7 @@ class ResearchTableProviderContract:
|
|
|
30
30
|
key: str
|
|
31
31
|
display_name: str
|
|
32
32
|
kind: str
|
|
33
|
+
behavior_class: str = "standard_enrichment"
|
|
33
34
|
provider_aliases: Tuple[str, ...] = ()
|
|
34
35
|
origin_aliases: Tuple[str, ...] = ()
|
|
35
36
|
recipe_type: Optional[str] = None
|
|
@@ -39,6 +40,7 @@ class ResearchTableProviderContract:
|
|
|
39
40
|
setup_contract: Dict[str, Any] = field(default_factory=dict)
|
|
40
41
|
execution_policy: Optional[AutoRunExecutionPolicy] = None
|
|
41
42
|
dispatch_strategy: str = "bulk_queue"
|
|
43
|
+
readiness_strategy: Optional[str] = None
|
|
42
44
|
save_time_entrypoint: Optional[str] = None
|
|
43
45
|
is_action_provider: bool = False
|
|
44
46
|
composition_contract: Dict[str, Any] = field(default_factory=dict)
|
|
@@ -72,10 +74,12 @@ class ResearchTableProviderContract:
|
|
|
72
74
|
payload: Dict[str, Any] = {
|
|
73
75
|
"display_name": self.display_name,
|
|
74
76
|
"kind": self.kind,
|
|
77
|
+
"behavior_class": self.behavior_class,
|
|
75
78
|
"recipe_type": self.recipe_type,
|
|
76
79
|
"provider_aliases": list(self.provider_aliases or ()),
|
|
77
80
|
"origin_aliases": list(self.origin_aliases or ()),
|
|
78
81
|
"dispatch_strategy": self.dispatch_strategy,
|
|
82
|
+
"readiness_strategy": self.readiness_strategy,
|
|
79
83
|
"action_provider": bool(self.is_action_provider),
|
|
80
84
|
"setup_contract": deepcopy(self.setup_contract),
|
|
81
85
|
"composition_contract": deepcopy(self.composition_contract),
|
|
@@ -147,6 +151,12 @@ def _text(value: Any) -> str:
|
|
|
147
151
|
return str(value or "").strip()
|
|
148
152
|
|
|
149
153
|
|
|
154
|
+
def _mapping_text(value: Any) -> str:
|
|
155
|
+
if isinstance(value, dict):
|
|
156
|
+
return _text(value.get("column"))
|
|
157
|
+
return _text(value)
|
|
158
|
+
|
|
159
|
+
|
|
150
160
|
def _llm_provider_values() -> Tuple[str, ...]:
|
|
151
161
|
return ("gemini", "xai", "openai", "anthropic", "groq")
|
|
152
162
|
|
|
@@ -271,6 +281,7 @@ _ADD_TO_SEQUENCE_POLICY = AutoRunExecutionPolicy(
|
|
|
271
281
|
force_rerun=False,
|
|
272
282
|
respect_existing_done=True,
|
|
273
283
|
default_batch_size=50,
|
|
284
|
+
batch_size_env="ADD_TO_SEQUENCE_BATCH_SIZE",
|
|
274
285
|
)
|
|
275
286
|
|
|
276
287
|
_HTTP_REQUEST_POLICY = AutoRunExecutionPolicy(
|
|
@@ -293,6 +304,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
293
304
|
key="formatter",
|
|
294
305
|
display_name="Formatter",
|
|
295
306
|
kind="formatter",
|
|
307
|
+
behavior_class="formatter",
|
|
296
308
|
recipe_type="formatter",
|
|
297
309
|
recipe_payload={
|
|
298
310
|
"key": "engagement_statement",
|
|
@@ -343,6 +355,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
343
355
|
key="llm",
|
|
344
356
|
display_name="LLM Enrichment",
|
|
345
357
|
kind="enrichment",
|
|
358
|
+
behavior_class="standard_enrichment",
|
|
346
359
|
provider_aliases=_llm_provider_values(),
|
|
347
360
|
recipe_type="llm_enrichment",
|
|
348
361
|
recipe_payload={
|
|
@@ -365,8 +378,10 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
365
378
|
},
|
|
366
379
|
recipe_notes=(
|
|
367
380
|
"Recommended default for agent mode: provide instructions and let the API compile the runnable prompt.",
|
|
381
|
+
"When the platform generates the runnable prompt from instructions, keep useAutoSchema=true unless you intentionally want to own the schema yourself.",
|
|
382
|
+
"Do not send user_schema/response_schema in that default generated-prompt path; prompt/schema drift is possible if you later change the generated prompt but keep a frozen custom schema.",
|
|
368
383
|
"Basic is for extraction, scoring, or classification using fields already in the row. Agent is for tasks that may need outside lookup or web research.",
|
|
369
|
-
"Create/update always materializes the runnable prompt into config.
|
|
384
|
+
"Create/update always materializes the runnable prompt into config.advancedPrompt before execution.",
|
|
370
385
|
"Generated agent-mode prompts always include company/requester context.",
|
|
371
386
|
"Basic mode stays manual-prompt-first; write the prompt directly from the user goal/preferences.",
|
|
372
387
|
"Runtime only injects values for placeholders the prompt explicitly references.",
|
|
@@ -431,10 +446,10 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
431
446
|
"generated": {
|
|
432
447
|
"instructions_field": "config.instructions",
|
|
433
448
|
"prompt_source": "generated",
|
|
434
|
-
"materialized_prompt_fields": ["config.
|
|
449
|
+
"materialized_prompt_fields": ["config.advancedPrompt"],
|
|
435
450
|
},
|
|
436
451
|
"manual": {
|
|
437
|
-
"prompt_field": "config.
|
|
452
|
+
"prompt_field": "config.advancedPrompt",
|
|
438
453
|
"prompt_source": "manual",
|
|
439
454
|
},
|
|
440
455
|
},
|
|
@@ -477,13 +492,14 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
477
492
|
],
|
|
478
493
|
},
|
|
479
494
|
execution_policy=_LLM_POLICY,
|
|
480
|
-
dispatch_strategy="
|
|
495
|
+
dispatch_strategy="bulk_queue",
|
|
481
496
|
save_time_entrypoint="provider_save_hook",
|
|
482
497
|
),
|
|
483
498
|
ResearchTableProviderContract(
|
|
484
499
|
key="email_finder",
|
|
485
500
|
display_name="Email Finder",
|
|
486
501
|
kind="enrichment",
|
|
502
|
+
behavior_class="standard_enrichment",
|
|
487
503
|
provider_aliases=("smart_email_finder", "email_validator", "email_finder"),
|
|
488
504
|
origin_aliases=("email_finder",),
|
|
489
505
|
recipe_type="email_finder",
|
|
@@ -505,6 +521,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
505
521
|
},
|
|
506
522
|
recipe_notes=(
|
|
507
523
|
"For non-LinkedIn lookup, use lookupStrategy=domain/company with firstName+lastName+domain/company fields.",
|
|
524
|
+
"Primitive source columns map directly. If an upstream source column stores structured JSON, map it as an object with column + path, for example {\"column\": \"linkedin_lookup\", \"path\": \"linkedin_url\"}.",
|
|
508
525
|
),
|
|
509
526
|
column_type={
|
|
510
527
|
"type": "email_finder",
|
|
@@ -531,6 +548,13 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
531
548
|
"strategy_field": "config.strategy",
|
|
532
549
|
"lookup_strategy_field": "config.lookupStrategy",
|
|
533
550
|
"input_fields": ["config.linkedinUrl", "config.firstName", "config.lastName", "config.company", "config.domain"],
|
|
551
|
+
"structured_source_mapping": {
|
|
552
|
+
"rule": "primitive columns map directly; structured columns use {column, path}",
|
|
553
|
+
"examples": [
|
|
554
|
+
{"linkedinUrl": {"column": "linkedin_lookup", "path": "linkedin_url"}},
|
|
555
|
+
{"emailColumn": {"column": "email_validation", "path": "response"}},
|
|
556
|
+
],
|
|
557
|
+
},
|
|
534
558
|
},
|
|
535
559
|
composition_contract={
|
|
536
560
|
"consumes": {
|
|
@@ -552,6 +576,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
552
576
|
key="phone_finder",
|
|
553
577
|
display_name="Phone Finder",
|
|
554
578
|
kind="enrichment",
|
|
579
|
+
behavior_class="standard_enrichment",
|
|
555
580
|
provider_aliases=("smart_phone_finder", "phone_validator", "phone_finder"),
|
|
556
581
|
origin_aliases=("phone_finder",),
|
|
557
582
|
recipe_type="phone_finder",
|
|
@@ -572,6 +597,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
572
597
|
},
|
|
573
598
|
recipe_notes=(
|
|
574
599
|
"You can use linkedinUrl or email/workEmail/personalEmail inputs.",
|
|
600
|
+
"Primitive source columns map directly. If an upstream source column stores structured JSON, map it as an object with column + path, for example {\"column\": \"linkedin_lookup\", \"path\": \"linkedin_url\"}.",
|
|
575
601
|
),
|
|
576
602
|
column_type={
|
|
577
603
|
"type": "phone_finder",
|
|
@@ -605,6 +631,13 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
605
631
|
"config.lastName",
|
|
606
632
|
"config.company",
|
|
607
633
|
],
|
|
634
|
+
"structured_source_mapping": {
|
|
635
|
+
"rule": "primitive columns map directly; structured columns use {column, path}",
|
|
636
|
+
"examples": [
|
|
637
|
+
{"linkedinUrl": {"column": "linkedin_lookup", "path": "linkedin_url"}},
|
|
638
|
+
{"phoneColumn": {"column": "mobile_phone", "path": "mobile_number"}},
|
|
639
|
+
],
|
|
640
|
+
},
|
|
608
641
|
},
|
|
609
642
|
composition_contract={
|
|
610
643
|
"consumes": {
|
|
@@ -626,6 +659,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
626
659
|
key="lead_finder",
|
|
627
660
|
display_name="Lead Finder",
|
|
628
661
|
kind="enrichment",
|
|
662
|
+
behavior_class="standard_enrichment",
|
|
629
663
|
provider_aliases=("lead_finder",),
|
|
630
664
|
recipe_type="lead_finder",
|
|
631
665
|
recipe_payload={
|
|
@@ -650,6 +684,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
650
684
|
},
|
|
651
685
|
recipe_notes=(
|
|
652
686
|
"Add more targeting keys as needed (jobFunctions, seniorities, keywords, excludeKeywords, skills).",
|
|
687
|
+
"Primitive source columns map directly. If companyDomain or other mapped inputs come from structured JSON, use {\"column\": \"...\", \"path\": \"...\"} instead of a plain string column key.",
|
|
653
688
|
),
|
|
654
689
|
column_type={
|
|
655
690
|
"type": "lead_finder",
|
|
@@ -675,6 +710,12 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
675
710
|
"source_mode_field": "config.sourceMode",
|
|
676
711
|
"company_domain_field": "config.companyDomain",
|
|
677
712
|
"max_results_field": "config.maxResults",
|
|
713
|
+
"structured_source_mapping": {
|
|
714
|
+
"rule": "primitive columns map directly; structured columns use {column, path}",
|
|
715
|
+
"examples": [
|
|
716
|
+
{"companyDomain": {"column": "company_lookup", "path": "domain"}},
|
|
717
|
+
],
|
|
718
|
+
},
|
|
678
719
|
},
|
|
679
720
|
composition_contract={
|
|
680
721
|
"consumes": {
|
|
@@ -706,6 +747,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
706
747
|
key="add_to_crm",
|
|
707
748
|
display_name="Add to Leads",
|
|
708
749
|
kind="enrichment",
|
|
750
|
+
behavior_class="action_enrichment",
|
|
709
751
|
provider_aliases=("add_to_crm",),
|
|
710
752
|
recipe_type="add_to_crm",
|
|
711
753
|
recipe_payload={
|
|
@@ -745,7 +787,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
745
787
|
"companyDomain is required; LinkedIn is optional.",
|
|
746
788
|
"Include at least one usable hard-identity mapping such as LinkedIn, email, or phone.",
|
|
747
789
|
"If companyDomain is missing in the table, derive or enrich that domain column first.",
|
|
748
|
-
"Primitive source columns map directly.
|
|
790
|
+
"Primitive source columns map directly. If a mapped source column stores structured JSON, send an object with column + path, for example {\"column\": \"work_email\", \"path\": \"response\"} or {\"column\": \"mobile_phone\", \"path\": \"mobile_number\"}.",
|
|
749
791
|
"Add to CRM is optional and non-billable.",
|
|
750
792
|
"Creating add_to_crm after upstream email/phone/lead columns already finished does not replay those older source updates; run add_to_crm explicitly or rerun the upstream source column.",
|
|
751
793
|
),
|
|
@@ -772,18 +814,19 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
772
814
|
"eligibility": "company domain plus at least one valid LinkedIn URL, email, or phone must resolve from mapped source columns",
|
|
773
815
|
"replay_behavior": "source updates only; explicit run required to backfill older upstream results",
|
|
774
816
|
"structured_source_mapping": {
|
|
775
|
-
"rule": "primitive columns map directly; structured columns
|
|
776
|
-
"field": "config.fieldMappings
|
|
817
|
+
"rule": "primitive columns map directly; structured columns use {column, path}",
|
|
818
|
+
"field": "config.fieldMappings.*",
|
|
777
819
|
"examples": [
|
|
778
|
-
"emailAddresses[
|
|
779
|
-
"phoneNumbers[
|
|
820
|
+
{"emailAddresses": [{"column": "work_email", "path": "response", "type": "work"}]},
|
|
821
|
+
{"phoneNumbers": [{"column": "mobile_phone", "path": "mobile_number", "type": "mobile"}]},
|
|
822
|
+
{"linkedinUrl": {"column": "linkedin_lookup", "path": "linkedin_url"}},
|
|
780
823
|
],
|
|
781
824
|
},
|
|
782
825
|
},
|
|
783
826
|
composition_contract={
|
|
784
827
|
"consumes": {
|
|
785
828
|
"kind": "crm_export_inputs",
|
|
786
|
-
"shape": "mapped source row values including company domain plus LinkedIn/email/phone identity; structured source columns
|
|
829
|
+
"shape": "mapped source row values including company domain plus LinkedIn/email/phone identity; structured source columns use explicit {column, path} mappings to identify the exact field",
|
|
787
830
|
},
|
|
788
831
|
"produces": {
|
|
789
832
|
"kind": "lead_ids",
|
|
@@ -794,12 +837,14 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
794
837
|
],
|
|
795
838
|
},
|
|
796
839
|
execution_policy=_ADD_TO_CRM_POLICY,
|
|
840
|
+
readiness_strategy="add_to_crm",
|
|
797
841
|
is_action_provider=True,
|
|
798
842
|
),
|
|
799
843
|
ResearchTableProviderContract(
|
|
800
844
|
key="sync_to_table",
|
|
801
845
|
display_name="Sync to Table",
|
|
802
846
|
kind="enrichment",
|
|
847
|
+
behavior_class="action_enrichment",
|
|
803
848
|
provider_aliases=("sync_to_table",),
|
|
804
849
|
recipe_type="sync_to_table",
|
|
805
850
|
recipe_payload={
|
|
@@ -904,12 +949,14 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
904
949
|
},
|
|
905
950
|
execution_policy=_SYNC_TO_TABLE_POLICY,
|
|
906
951
|
dispatch_strategy="sync_to_table_prefilter",
|
|
952
|
+
readiness_strategy="sync_to_table",
|
|
907
953
|
is_action_provider=True,
|
|
908
954
|
),
|
|
909
955
|
ResearchTableProviderContract(
|
|
910
956
|
key="add_to_sequence",
|
|
911
957
|
display_name="Add to Sequence",
|
|
912
958
|
kind="enrichment",
|
|
959
|
+
behavior_class="action_enrichment",
|
|
913
960
|
provider_aliases=("add_to_sequence",),
|
|
914
961
|
recipe_type="add_to_sequence",
|
|
915
962
|
recipe_payload={
|
|
@@ -972,12 +1019,14 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
972
1019
|
],
|
|
973
1020
|
},
|
|
974
1021
|
execution_policy=_ADD_TO_SEQUENCE_POLICY,
|
|
1022
|
+
readiness_strategy="add_to_sequence",
|
|
975
1023
|
is_action_provider=True,
|
|
976
1024
|
),
|
|
977
1025
|
ResearchTableProviderContract(
|
|
978
1026
|
key="http_request",
|
|
979
1027
|
display_name="HTTP Request",
|
|
980
1028
|
kind="enrichment",
|
|
1029
|
+
behavior_class="standard_enrichment",
|
|
981
1030
|
provider_aliases=("http_request",),
|
|
982
1031
|
recipe_type="http_request",
|
|
983
1032
|
recipe_payload={
|
|
@@ -998,7 +1047,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
998
1047
|
},
|
|
999
1048
|
recipe_notes=(
|
|
1000
1049
|
"Use {{column_key}} placeholders in config.url, config.headers, and config.body to reference row values.",
|
|
1001
|
-
"Use {{secrets.name}} to resolve a workspace secret
|
|
1050
|
+
"Use {{secrets.name}} to resolve a workspace secret by name.",
|
|
1002
1051
|
"body can be null, a string template, or an object whose values are templated and sent as JSON.",
|
|
1003
1052
|
"The stored cell value is the parsed response body (JSON or text), not the full transport envelope.",
|
|
1004
1053
|
"Preview the resolved request with `autotouch columns test-http-request --table-id <TABLE_ID> --data-file column.json` before creating or running the column.",
|
|
@@ -1038,7 +1087,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
|
|
|
1038
1087
|
composition_contract={
|
|
1039
1088
|
"consumes": {
|
|
1040
1089
|
"kind": "row_data",
|
|
1041
|
-
"shape": "{{column_key}} templates resolved from row cell values plus optional {{secrets.name}}
|
|
1090
|
+
"shape": "{{column_key}} templates resolved from row cell values plus optional {{secrets.name}} workspace secrets",
|
|
1042
1091
|
},
|
|
1043
1092
|
"produces": {
|
|
1044
1093
|
"kind": "json",
|
|
@@ -1257,30 +1306,39 @@ def validate_column_provider_contract(
|
|
|
1257
1306
|
|
|
1258
1307
|
if contract.key == "email_finder":
|
|
1259
1308
|
if not any(
|
|
1260
|
-
|
|
1261
|
-
for field_name in ("linkedinUrl", "firstName", "lastName", "company", "domain")
|
|
1309
|
+
_mapping_text(cfg.get(field_name))
|
|
1310
|
+
for field_name in ("linkedinUrl", "firstName", "lastName", "company", "domain", "emailColumn")
|
|
1262
1311
|
):
|
|
1263
1312
|
raise ProviderContractValidationError(
|
|
1264
1313
|
"missing_required_field",
|
|
1265
|
-
"email_finder requires at least one lookup input such as config.linkedinUrl, config.domain, or name/company fields.",
|
|
1314
|
+
"email_finder requires at least one lookup input such as config.linkedinUrl, config.domain, config.emailColumn, or name/company fields.",
|
|
1266
1315
|
hint="Use `autotouch columns recipe --type email_finder`.",
|
|
1267
1316
|
)
|
|
1268
1317
|
return contract
|
|
1269
1318
|
|
|
1270
1319
|
if contract.key == "phone_finder":
|
|
1271
1320
|
if not any(
|
|
1272
|
-
|
|
1273
|
-
for field_name in (
|
|
1321
|
+
_mapping_text(cfg.get(field_name))
|
|
1322
|
+
for field_name in (
|
|
1323
|
+
"linkedinUrl",
|
|
1324
|
+
"email",
|
|
1325
|
+
"workEmail",
|
|
1326
|
+
"personalEmail",
|
|
1327
|
+
"firstName",
|
|
1328
|
+
"lastName",
|
|
1329
|
+
"company",
|
|
1330
|
+
"phoneColumn",
|
|
1331
|
+
)
|
|
1274
1332
|
):
|
|
1275
1333
|
raise ProviderContractValidationError(
|
|
1276
1334
|
"missing_required_field",
|
|
1277
|
-
"phone_finder requires at least one lookup input such as config.linkedinUrl
|
|
1335
|
+
"phone_finder requires at least one lookup input such as config.linkedinUrl, config.email/workEmail, or config.phoneColumn.",
|
|
1278
1336
|
hint="Use `autotouch columns recipe --type phone_finder`.",
|
|
1279
1337
|
)
|
|
1280
1338
|
return contract
|
|
1281
1339
|
|
|
1282
1340
|
if contract.key == "lead_finder":
|
|
1283
|
-
if not
|
|
1341
|
+
if not _mapping_text(cfg.get("companyDomain")):
|
|
1284
1342
|
raise ProviderContractValidationError(
|
|
1285
1343
|
"missing_required_field",
|
|
1286
1344
|
"lead_finder requires config.companyDomain.",
|
|
@@ -1308,7 +1366,7 @@ def validate_column_provider_contract(
|
|
|
1308
1366
|
raise ProviderContractValidationError(
|
|
1309
1367
|
"invalid_field",
|
|
1310
1368
|
"http_request config.headers must be an object when provided.",
|
|
1311
|
-
hint="Set config.headers to a JSON object such as {\"Authorization\": \"Bearer {{secrets.
|
|
1369
|
+
hint="Set config.headers to a JSON object such as {\"Authorization\": \"Bearer {{secrets.openai_api_key}}\"}.",
|
|
1312
1370
|
)
|
|
1313
1371
|
body = cfg.get("body")
|
|
1314
1372
|
if body is not None and not isinstance(body, (str, dict)):
|
|
@@ -1355,3 +1413,74 @@ def automation_workflow_blueprints() -> Dict[str, Dict[str, Any]]:
|
|
|
1355
1413
|
|
|
1356
1414
|
def action_provider_keys() -> Tuple[str, ...]:
|
|
1357
1415
|
return tuple(contract.key for contract in _PROVIDER_CONTRACTS if contract.is_action_provider)
|
|
1416
|
+
|
|
1417
|
+
|
|
1418
|
+
def behavior_class_keys(behavior_class: str) -> Tuple[str, ...]:
|
|
1419
|
+
normalized = _normalize(behavior_class)
|
|
1420
|
+
return tuple(
|
|
1421
|
+
contract.key
|
|
1422
|
+
for contract in _PROVIDER_CONTRACTS
|
|
1423
|
+
if _normalize(contract.behavior_class) == normalized
|
|
1424
|
+
)
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
def orchestrated_runtime_provider_keys(*, include_formatter: bool = False) -> Tuple[str, ...]:
|
|
1428
|
+
providers: List[str] = []
|
|
1429
|
+
for contract in _PROVIDER_CONTRACTS:
|
|
1430
|
+
policy = contract.execution_policy
|
|
1431
|
+
if not policy:
|
|
1432
|
+
continue
|
|
1433
|
+
normalized_behavior = _normalize(contract.behavior_class)
|
|
1434
|
+
if normalized_behavior == "formatter":
|
|
1435
|
+
if include_formatter:
|
|
1436
|
+
providers.append(policy.orchestrator_provider)
|
|
1437
|
+
continue
|
|
1438
|
+
if contract.key == "llm":
|
|
1439
|
+
providers.extend(["gemini", "xai"])
|
|
1440
|
+
continue
|
|
1441
|
+
providers.append(policy.orchestrator_provider)
|
|
1442
|
+
return tuple(dict.fromkeys(provider for provider in providers if provider))
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
def resolve_provider_behavior_class(
|
|
1446
|
+
column: Dict[str, Any],
|
|
1447
|
+
config: Optional[Dict[str, Any]],
|
|
1448
|
+
) -> Optional[str]:
|
|
1449
|
+
contract = resolve_effective_provider_contract(column, config)
|
|
1450
|
+
if not contract:
|
|
1451
|
+
return None
|
|
1452
|
+
return contract.behavior_class
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
def should_resolve_managed_llm_model(
|
|
1456
|
+
column: Dict[str, Any],
|
|
1457
|
+
config: Optional[Dict[str, Any]],
|
|
1458
|
+
) -> bool:
|
|
1459
|
+
contract = resolve_effective_provider_contract(column, config)
|
|
1460
|
+
if not contract:
|
|
1461
|
+
return False
|
|
1462
|
+
return contract.key == "llm" and _normalize(column.get("origin")) == "ai"
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
def resolve_runtime_orchestrator_provider(
|
|
1466
|
+
column: Dict[str, Any],
|
|
1467
|
+
config: Optional[Dict[str, Any]],
|
|
1468
|
+
) -> Optional[str]:
|
|
1469
|
+
contract = resolve_effective_provider_contract(column, config)
|
|
1470
|
+
if not contract or not contract.execution_policy:
|
|
1471
|
+
return None
|
|
1472
|
+
if _normalize(contract.behavior_class) == "formatter":
|
|
1473
|
+
return None
|
|
1474
|
+
if contract.key == "llm":
|
|
1475
|
+
runtime_provider = _normalize((config or {}).get("provider"))
|
|
1476
|
+
if runtime_provider in {"gemini", "xai"}:
|
|
1477
|
+
return runtime_provider
|
|
1478
|
+
return None
|
|
1479
|
+
return contract.execution_policy.orchestrator_provider
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
def uses_orchestrated_bulk_run(
|
|
1483
|
+
column: Dict[str, Any],
|
|
1484
|
+
config: Optional[Dict[str, Any]],
|
|
1485
|
+
) -> bool:
|
|
1486
|
+
return resolve_runtime_orchestrator_provider(column, config) is not None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|