pltr-cli 0.14.1__tar.gz → 0.15.0__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.
Files changed (190) hide show
  1. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/PKG-INFO +2 -1
  2. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/mypy.ini +4 -0
  3. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/pyproject.toml +2 -1
  4. pltr_cli-0.15.0/src/pltr/__init__.py +1 -0
  5. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/language_models.py +147 -5
  6. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/ontology.py +106 -0
  7. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/language_models.py +272 -7
  8. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/ontology.py +170 -1
  9. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/sql.py +26 -2
  10. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_language_models.py +176 -0
  11. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_ontology.py +183 -0
  12. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_language_models.py +338 -7
  13. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_ontology.py +195 -0
  14. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_sql.py +33 -0
  15. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/uv.lock +119 -1
  16. pltr_cli-0.14.1/src/pltr/__init__.py +0 -1
  17. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.github/workflows/ci.yml +0 -0
  18. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.github/workflows/claude-code-review.yml +0 -0
  19. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.github/workflows/claude.yml +0 -0
  20. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.github/workflows/publish.yml +0 -0
  21. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.github/workflows/test-publish.yml +0 -0
  22. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.gitignore +0 -0
  23. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/.pre-commit-config.yaml +0 -0
  24. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/CHANGELOG.md +0 -0
  25. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/CLAUDE.md +0 -0
  26. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/LICENSE +0 -0
  27. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/README.md +0 -0
  28. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/RELEASE.md +0 -0
  29. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/README.md +0 -0
  30. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/SKILL.md +0 -0
  31. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/admin-commands.md +0 -0
  32. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/aip-agents-commands.md +0 -0
  33. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/connectivity-commands.md +0 -0
  34. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/dataset-commands.md +0 -0
  35. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/filesystem-commands.md +0 -0
  36. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/functions-commands.md +0 -0
  37. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/language-models-commands.md +0 -0
  38. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/mediasets-commands.md +0 -0
  39. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/models-commands.md +0 -0
  40. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/ontology-commands.md +0 -0
  41. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/orchestration-commands.md +0 -0
  42. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/quick-start.md +0 -0
  43. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/sql-commands.md +0 -0
  44. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/reference/streams-commands.md +0 -0
  45. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/workflows/data-analysis.md +0 -0
  46. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/workflows/data-pipeline.md +0 -0
  47. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/claude_skill/workflows/permission-management.md +0 -0
  48. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/README.md +0 -0
  49. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/api/wrapper.md +0 -0
  50. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/examples/csv-upload.md +0 -0
  51. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/examples/gallery.md +0 -0
  52. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/features/dataset-transactions.md +0 -0
  53. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/migration/v2-pagination.md +0 -0
  54. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/pagination.md +0 -0
  55. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/aliases.md +0 -0
  56. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/authentication.md +0 -0
  57. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/claude-skill.md +0 -0
  58. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/commands.md +0 -0
  59. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/quick-start.md +0 -0
  60. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/troubleshooting.md +0 -0
  61. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/docs/user-guide/workflows.md +0 -0
  62. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/scripts/release.py +0 -0
  63. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/__main__.py +0 -0
  64. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/auth/__init__.py +0 -0
  65. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/auth/base.py +0 -0
  66. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/auth/manager.py +0 -0
  67. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/auth/oauth.py +0 -0
  68. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/auth/storage.py +0 -0
  69. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/auth/token.py +0 -0
  70. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/cli.py +0 -0
  71. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/__init__.py +0 -0
  72. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/admin.py +0 -0
  73. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/aip_agents.py +0 -0
  74. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/alias.py +0 -0
  75. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/audit.py +0 -0
  76. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/completion.py +0 -0
  77. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/configure.py +0 -0
  78. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/connectivity.py +0 -0
  79. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/cp.py +0 -0
  80. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/data_health.py +0 -0
  81. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/dataset.py +0 -0
  82. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/folder.py +0 -0
  83. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/functions.py +0 -0
  84. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/mediasets.py +0 -0
  85. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/models.py +0 -0
  86. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/orchestration.py +0 -0
  87. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/project.py +0 -0
  88. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/resource.py +0 -0
  89. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/resource_role.py +0 -0
  90. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/shell.py +0 -0
  91. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/space.py +0 -0
  92. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/sql.py +0 -0
  93. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/streams.py +0 -0
  94. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/third_party_applications.py +0 -0
  95. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/verify.py +0 -0
  96. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/commands/widgets.py +0 -0
  97. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/config/__init__.py +0 -0
  98. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/config/aliases.py +0 -0
  99. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/config/profiles.py +0 -0
  100. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/config/settings.py +0 -0
  101. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/__init__.py +0 -0
  102. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/admin.py +0 -0
  103. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/aip_agents.py +0 -0
  104. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/audit.py +0 -0
  105. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/base.py +0 -0
  106. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/connectivity.py +0 -0
  107. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/copy.py +0 -0
  108. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/data_health.py +0 -0
  109. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/dataset.py +0 -0
  110. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/folder.py +0 -0
  111. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/functions.py +0 -0
  112. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/mediasets.py +0 -0
  113. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/models.py +0 -0
  114. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/orchestration.py +0 -0
  115. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/project.py +0 -0
  116. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/resource.py +0 -0
  117. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/resource_role.py +0 -0
  118. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/space.py +0 -0
  119. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/streams.py +0 -0
  120. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/third_party_applications.py +0 -0
  121. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/services/widgets.py +0 -0
  122. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/utils/__init__.py +0 -0
  123. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/utils/alias_resolver.py +0 -0
  124. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/utils/completion.py +0 -0
  125. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/utils/formatting.py +0 -0
  126. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/utils/pagination.py +0 -0
  127. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/src/pltr/utils/progress.py +0 -0
  128. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/__init__.py +0 -0
  129. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/conftest.py +0 -0
  130. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/README.md +0 -0
  131. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/__init__.py +0 -0
  132. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/conftest.py +0 -0
  133. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/test_auth_flow.py +0 -0
  134. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/test_cli_integration.py +0 -0
  135. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/test_data_workflows.py +0 -0
  136. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/test_data_workflows_simple.py +0 -0
  137. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/integration/test_simple_integration.py +0 -0
  138. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_auth/__init__.py +0 -0
  139. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_auth/test_base.py +0 -0
  140. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_auth/test_manager.py +0 -0
  141. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_auth/test_oauth.py +0 -0
  142. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_auth/test_storage.py +0 -0
  143. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_auth/test_token.py +0 -0
  144. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/__init__.py +0 -0
  145. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_admin.py +0 -0
  146. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_aip_agents.py +0 -0
  147. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_alias.py +0 -0
  148. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_audit.py +0 -0
  149. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_completion.py +0 -0
  150. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_connectivity.py +0 -0
  151. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_data_health.py +0 -0
  152. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_dataset.py +0 -0
  153. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_folder.py +0 -0
  154. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_functions.py +0 -0
  155. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_mediasets.py +0 -0
  156. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_models.py +0 -0
  157. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_orchestration.py +0 -0
  158. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_shell.py +0 -0
  159. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_sql.py +0 -0
  160. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_streams.py +0 -0
  161. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_third_party_applications.py +0 -0
  162. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_verify_simple.py +0 -0
  163. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_commands/test_widgets.py +0 -0
  164. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_config/__init__.py +0 -0
  165. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_config/test_aliases.py +0 -0
  166. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_config/test_profiles.py +0 -0
  167. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_config/test_settings.py +0 -0
  168. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/__init__.py +0 -0
  169. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_admin.py +0 -0
  170. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_aip_agents.py +0 -0
  171. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_base.py +0 -0
  172. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_connectivity.py +0 -0
  173. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_copy.py +0 -0
  174. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_data_health.py +0 -0
  175. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_dataset.py +0 -0
  176. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_dataset_transactions.py +0 -0
  177. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_folder.py +0 -0
  178. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_functions.py +0 -0
  179. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_mediasets.py +0 -0
  180. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_models.py +0 -0
  181. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_orchestration.py +0 -0
  182. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_pagination_integration.py +0 -0
  183. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_project.py +0 -0
  184. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_resource.py +0 -0
  185. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_resource_role.py +0 -0
  186. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_space.py +0 -0
  187. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_services/test_streams.py +0 -0
  188. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_utils/__init__.py +0 -0
  189. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_utils/test_alias_resolver.py +0 -0
  190. {pltr_cli-0.14.1 → pltr_cli-0.15.0}/tests/test_utils/test_pagination.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pltr-cli
3
- Version: 0.14.1
3
+ Version: 0.15.0
4
4
  Summary: Command-line interface for Palantir Foundry APIs
5
5
  Project-URL: Homepage, https://github.com/anjor/pltr-cli
6
6
  Project-URL: Repository, https://github.com/anjor/pltr-cli
@@ -30,6 +30,7 @@ Requires-Dist: click-repl>=0.3.0
30
30
  Requires-Dist: foundry-platform-sdk<2.0.0,>=1.69.0
31
31
  Requires-Dist: keyring>=25.6.0
32
32
  Requires-Dist: pandas>=2.0.0
33
+ Requires-Dist: pyarrow>=15.0.0
33
34
  Requires-Dist: python-dotenv>=1.1.1
34
35
  Requires-Dist: requests>=2.32.4
35
36
  Requires-Dist: rich>=14.1.0
@@ -10,3 +10,7 @@ disallow_untyped_decorators = False
10
10
  # Ignore missing imports for foundry SDK
11
11
  [mypy-foundry.*]
12
12
  ignore_missing_imports = True
13
+
14
+ # pyarrow ships no py.typed marker
15
+ [mypy-pyarrow.*]
16
+ ignore_missing_imports = True
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pltr-cli"
3
- version = "0.14.1"
3
+ version = "0.15.0"
4
4
  description = "Command-line interface for Palantir Foundry APIs"
5
5
  authors = [
6
6
  { name = "anjor", email = "anjor@umd.edu" },
@@ -12,6 +12,7 @@ dependencies = [
12
12
  "foundry-platform-sdk>=1.69.0,<2.0.0",
13
13
  "keyring>=25.6.0",
14
14
  "pandas>=2.0.0",
15
+ "pyarrow>=15.0.0",
15
16
  "python-dotenv>=1.1.1",
16
17
  "requests>=2.32.4",
17
18
  "rich>=14.1.0",
@@ -0,0 +1 @@
1
+ __version__ = "0.15.0"
@@ -123,6 +123,148 @@ def display_openai_response(response: dict, format: str, output: Optional[str]):
123
123
  formatter.display(response, format)
124
124
 
125
125
 
126
+ # ===== Shared Language Model Commands =====
127
+
128
+
129
+ @app.command("list")
130
+ def list_models(
131
+ profile: Optional[str] = typer.Option(
132
+ None,
133
+ "--profile",
134
+ "-p",
135
+ help="Profile name",
136
+ autocompletion=complete_profile,
137
+ ),
138
+ format: str = typer.Option(
139
+ "table",
140
+ "--format",
141
+ "-f",
142
+ help="Output format (table, json, csv)",
143
+ autocompletion=complete_output_format,
144
+ ),
145
+ output: Optional[str] = typer.Option(
146
+ None, "--output", "-o", help="Output file path"
147
+ ),
148
+ ):
149
+ """List available language models for the current enrollment."""
150
+ try:
151
+ from ..services.language_models import LanguageModelsService
152
+
153
+ service = LanguageModelsService(profile=profile)
154
+
155
+ with SpinnerProgressTracker().track_spinner("Fetching available models..."):
156
+ models = service.list_available_models()
157
+
158
+ formatter.format_table(
159
+ models,
160
+ columns=["model_rid", "status", "type", "display_name"],
161
+ format=format,
162
+ output=output,
163
+ )
164
+
165
+ if output:
166
+ formatter.print_success(f"Model list saved to {output}")
167
+
168
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
169
+ formatter.print_error(f"Authentication error: {e}")
170
+ raise typer.Exit(1)
171
+ except Exception as e:
172
+ formatter.print_error(f"Operation failed: {e}")
173
+ raise typer.Exit(1)
174
+
175
+
176
+ @app.command("status")
177
+ def model_status(
178
+ model_id: str = typer.Argument(
179
+ ...,
180
+ help="Model Resource Identifier (ri.language-model-service..language-model.<id>)",
181
+ ),
182
+ profile: Optional[str] = typer.Option(
183
+ None,
184
+ "--profile",
185
+ "-p",
186
+ help="Profile name",
187
+ autocompletion=complete_profile,
188
+ ),
189
+ format: str = typer.Option(
190
+ "table",
191
+ "--format",
192
+ "-f",
193
+ help="Output format (table, json, csv)",
194
+ autocompletion=complete_output_format,
195
+ ),
196
+ output: Optional[str] = typer.Option(
197
+ None, "--output", "-o", help="Output file path"
198
+ ),
199
+ ):
200
+ """Check enrollment status for a language model via direct API fallback endpoints."""
201
+ try:
202
+ from ..services.language_models import LanguageModelsService
203
+
204
+ service = LanguageModelsService(profile=profile)
205
+
206
+ with SpinnerProgressTracker().track_spinner("Checking model status..."):
207
+ result = service.get_model_enrollment_status(model_id)
208
+
209
+ formatter.format_dict(result, format=format, output=output)
210
+
211
+ if output:
212
+ formatter.print_success(f"Model status saved to {output}")
213
+
214
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
215
+ formatter.print_error(f"Authentication error: {e}")
216
+ raise typer.Exit(1)
217
+ except Exception as e:
218
+ formatter.print_error(f"Operation failed: {e}")
219
+ raise typer.Exit(1)
220
+
221
+
222
+ @app.command("enroll")
223
+ def enroll_model(
224
+ model_id: str = typer.Argument(
225
+ ...,
226
+ help="Model Resource Identifier (ri.language-model-service..language-model.<id>)",
227
+ ),
228
+ profile: Optional[str] = typer.Option(
229
+ None,
230
+ "--profile",
231
+ "-p",
232
+ help="Profile name",
233
+ autocompletion=complete_profile,
234
+ ),
235
+ format: str = typer.Option(
236
+ "table",
237
+ "--format",
238
+ "-f",
239
+ help="Output format (table, json, csv)",
240
+ autocompletion=complete_output_format,
241
+ ),
242
+ output: Optional[str] = typer.Option(
243
+ None, "--output", "-o", help="Output file path"
244
+ ),
245
+ ):
246
+ """Enroll/enable a language model via direct API fallback endpoints."""
247
+ try:
248
+ from ..services.language_models import LanguageModelsService
249
+
250
+ service = LanguageModelsService(profile=profile)
251
+
252
+ with SpinnerProgressTracker().track_spinner("Enrolling model..."):
253
+ result = service.enroll_model(model_id)
254
+
255
+ formatter.format_dict(result, format=format, output=output)
256
+
257
+ if output:
258
+ formatter.print_success(f"Enrollment result saved to {output}")
259
+
260
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
261
+ formatter.print_error(f"Authentication error: {e}")
262
+ raise typer.Exit(1)
263
+ except Exception as e:
264
+ formatter.print_error(f"Operation failed: {e}")
265
+ raise typer.Exit(1)
266
+
267
+
126
268
  # ===== Anthropic Commands =====
127
269
 
128
270
 
@@ -323,9 +465,9 @@ def anthropic_messages_advanced(
323
465
  # conversation.json:
324
466
  # {
325
467
  # "messages": [
326
- # {"role": "user", "content": [{"type": "text", "text": "Hi"}]},
327
- # {"role": "assistant", "content": [{"type": "text", "text": "Hello!"}]},
328
- # {"role": "user", "content": [{"type": "text", "text": "Help me"}]}
468
+ # {"role": "USER", "content": [{"type": "text", "text": "Hi"}]},
469
+ # {"role": "ASSISTANT", "content": [{"type": "text", "text": "Hello!"}]},
470
+ # {"role": "USER", "content": [{"type": "text", "text": "Help me"}]}
329
471
  # ],
330
472
  # "maxTokens": 500
331
473
  # }
@@ -334,11 +476,11 @@ def anthropic_messages_advanced(
334
476
 
335
477
  # Inline JSON with system prompt
336
478
  pltr language-models anthropic messages-advanced ri.language-models.main.model.abc123 \\
337
- --request '{"messages": [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}], "maxTokens": 100, "system": [{"type": "text", "text": "Be concise"}]}'
479
+ --request '{"messages": [{"role": "USER", "content": [{"type": "text", "text": "Hi"}]}], "maxTokens": 100, "system": [{"type": "text", "text": "Be concise"}]}'
338
480
 
339
481
  # With extended thinking
340
482
  pltr language-models anthropic messages-advanced ri.language-models.main.model.abc123 \\
341
- --request '{"messages": [{"role": "user", "content": [{"type": "text", "text": "Solve this problem"}]}], "maxTokens": 2000, "thinking": {"type": "enabled", "budget": 10000}}'
483
+ --request '{"messages": [{"role": "USER", "content": [{"type": "text", "text": "Solve this problem"}]}], "maxTokens": 2000, "thinking": {"type": "enabled", "budget": 10000}}'
342
484
  """
343
485
  try:
344
486
  # Parse request JSON
@@ -164,6 +164,112 @@ def get_object_type(
164
164
  raise typer.Exit(1)
165
165
 
166
166
 
167
+ @app.command("object-type-create")
168
+ def create_object_type(
169
+ ontology_rid: str = typer.Argument(..., help="Ontology Resource Identifier"),
170
+ api_name: str = typer.Option(..., "--api-name", help="Object type API name"),
171
+ display_name: str = typer.Option(
172
+ ..., "--display-name", help="Object type display name"
173
+ ),
174
+ primary_key: str = typer.Option(
175
+ ..., "--primary-key", help="Primary key property API name"
176
+ ),
177
+ backing_dataset: str = typer.Option(
178
+ ..., "--backing-dataset", help="Backing dataset RID"
179
+ ),
180
+ description: Optional[str] = typer.Option(
181
+ None, "--description", help="Object type description"
182
+ ),
183
+ profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
184
+ format: str = typer.Option(
185
+ "table", "--format", "-f", help="Output format (table, json, csv)"
186
+ ),
187
+ output: Optional[str] = typer.Option(
188
+ None, "--output", "-o", help="Output file path"
189
+ ),
190
+ ):
191
+ """Create a new object type in an ontology."""
192
+ try:
193
+ service = ObjectTypeService(profile=profile)
194
+
195
+ with SpinnerProgressTracker().track_spinner(
196
+ f"Creating object type {api_name}..."
197
+ ):
198
+ result = service.create_object_type(
199
+ ontology_rid=ontology_rid,
200
+ api_name=api_name,
201
+ display_name=display_name,
202
+ primary_key=primary_key,
203
+ backing_dataset=backing_dataset,
204
+ description=description,
205
+ )
206
+
207
+ formatter.format_dict(result, format=format, output=output)
208
+
209
+ if output:
210
+ formatter.print_success(f"Object type creation result saved to {output}")
211
+
212
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
213
+ formatter.print_error(f"Authentication error: {e}")
214
+ raise typer.Exit(1)
215
+ except Exception as e:
216
+ formatter.print_error(f"Failed to create object type: {e}")
217
+ raise typer.Exit(1)
218
+
219
+
220
+ @app.command("link-type-create")
221
+ def create_link_type(
222
+ ontology_rid: str = typer.Argument(..., help="Ontology Resource Identifier"),
223
+ api_name: str = typer.Option(..., "--api-name", help="Link type API name"),
224
+ from_object: str = typer.Option(..., "--from", help="Source object type API name"),
225
+ to_object: str = typer.Option(..., "--to", help="Target object type API name"),
226
+ display_name: Optional[str] = typer.Option(
227
+ None, "--display-name", help="Link type display name"
228
+ ),
229
+ description: Optional[str] = typer.Option(
230
+ None, "--description", help="Link type description"
231
+ ),
232
+ reverse_api_name: Optional[str] = typer.Option(
233
+ None, "--reverse-api-name", help="Reverse direction link type API name"
234
+ ),
235
+ profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
236
+ format: str = typer.Option(
237
+ "table", "--format", "-f", help="Output format (table, json, csv)"
238
+ ),
239
+ output: Optional[str] = typer.Option(
240
+ None, "--output", "-o", help="Output file path"
241
+ ),
242
+ ):
243
+ """Create a new link type in an ontology."""
244
+ try:
245
+ service = ObjectTypeService(profile=profile)
246
+
247
+ with SpinnerProgressTracker().track_spinner(
248
+ f"Creating link type {api_name}..."
249
+ ):
250
+ result = service.create_link_type(
251
+ ontology_rid=ontology_rid,
252
+ api_name=api_name,
253
+ from_object_type=from_object,
254
+ to_object_type=to_object,
255
+ display_name=display_name,
256
+ description=description,
257
+ reverse_api_name=reverse_api_name,
258
+ )
259
+
260
+ formatter.format_dict(result, format=format, output=output)
261
+
262
+ if output:
263
+ formatter.print_success(f"Link type creation result saved to {output}")
264
+
265
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
266
+ formatter.print_error(f"Authentication error: {e}")
267
+ raise typer.Exit(1)
268
+ except Exception as e:
269
+ formatter.print_error(f"Failed to create link type: {e}")
270
+ raise typer.Exit(1)
271
+
272
+
167
273
  # Object operations
168
274
  @app.command("object-list")
169
275
  def list_objects(
@@ -3,13 +3,29 @@ LanguageModels service wrapper for Foundry SDK.
3
3
  Provides access to Anthropic Claude models and OpenAI embeddings.
4
4
  """
5
5
 
6
- from typing import Any, Dict, List, Optional
6
+ from typing import Any, Dict, List, Optional, Union
7
+ import json
8
+ import requests
9
+ from urllib.parse import quote
7
10
  from .base import BaseService
8
11
 
9
12
 
10
13
  class LanguageModelsService(BaseService):
11
14
  """Service wrapper for Foundry LanguageModels operations."""
12
15
 
16
+ _MODEL_LIST_ENDPOINTS = [
17
+ "/v2/languageModels",
18
+ "/api/v2/llm/proxy/openai/v1/models",
19
+ ]
20
+ _MODEL_STATUS_ENDPOINTS = [
21
+ "/v2/languageModels/{model_id}",
22
+ "/api/v2/llm/models/{model_id}",
23
+ ]
24
+ _MODEL_ENROLL_ENDPOINTS = [
25
+ "/v2/languageModels/{model_id}/enroll",
26
+ "/api/v2/llm/models/{model_id}/enroll",
27
+ ]
28
+
13
29
  def _get_service(self) -> Any:
14
30
  """Get the Foundry LanguageModels service."""
15
31
  return self.client.language_models
@@ -71,7 +87,7 @@ class LanguageModelsService(BaseService):
71
87
  # Transform simple message to SDK message format
72
88
  messages = [
73
89
  {
74
- "role": "user",
90
+ "role": "USER",
75
91
  "content": [{"type": "text", "text": message}],
76
92
  }
77
93
  ]
@@ -134,7 +150,7 @@ class LanguageModelsService(BaseService):
134
150
  model_id: Model Resource Identifier
135
151
  Format: ri.language-models.main.model.<id>
136
152
  messages: List of message objects with role and content
137
- Format: [{"role": "user|assistant", "content": [...]}]
153
+ Format: [{"role": "USER|ASSISTANT", "content": [...]}]
138
154
  max_tokens: Maximum tokens to generate
139
155
  system: Optional system prompt blocks
140
156
  Format: [{"type": "text", "text": "..."}]
@@ -162,9 +178,9 @@ class LanguageModelsService(BaseService):
162
178
  Example:
163
179
  >>> service = LanguageModelsService()
164
180
  >>> messages = [
165
- ... {"role": "user", "content": [{"type": "text", "text": "Hi"}]},
166
- ... {"role": "assistant", "content": [{"type": "text", "text": "Hello!"}]},
167
- ... {"role": "user", "content": [{"type": "text", "text": "Help me"}]}
181
+ ... {"role": "USER", "content": [{"type": "text", "text": "Hi"}]},
182
+ ... {"role": "ASSISTANT", "content": [{"type": "text", "text": "Hello!"}]},
183
+ ... {"role": "USER", "content": [{"type": "text", "text": "Help me"}]}
168
184
  ... ]
169
185
  >>> response = service.send_messages_advanced(
170
186
  ... "ri.language-models.main.model.abc123",
@@ -173,9 +189,17 @@ class LanguageModelsService(BaseService):
173
189
  ... )
174
190
  """
175
191
  try:
192
+ normalized_messages: List[Dict[str, Any]] = []
193
+ for msg in messages:
194
+ normalized_msg = dict(msg)
195
+ role = normalized_msg.get("role")
196
+ if isinstance(role, str):
197
+ normalized_msg["role"] = role.upper()
198
+ normalized_messages.append(normalized_msg)
199
+
176
200
  # Build SDK kwargs
177
201
  request_kwargs: Dict[str, Any] = {
178
- "messages": messages,
202
+ "messages": normalized_messages,
179
203
  "max_tokens": max_tokens,
180
204
  "preview": preview,
181
205
  }
@@ -280,3 +304,244 @@ class LanguageModelsService(BaseService):
280
304
  raise RuntimeError(
281
305
  f"Failed to generate embeddings with model {model_id}: {e}"
282
306
  )
307
+
308
+ def list_available_models(self) -> List[Dict[str, Any]]:
309
+ """
310
+ List language models available to the authenticated user.
311
+
312
+ Tries the platform language-model listing endpoint first, then falls back
313
+ to the provider-compatible OpenAI models endpoint.
314
+ Note: This currently reads a single response per endpoint and does not
315
+ follow pagination tokens.
316
+
317
+ Returns:
318
+ List of model dictionaries with normalized keys:
319
+ - model_rid
320
+ - status
321
+ - type
322
+ - display_name
323
+
324
+ Raises:
325
+ RuntimeError: If no listing endpoint succeeds
326
+ """
327
+ last_error: Optional[Exception] = None
328
+
329
+ for endpoint in self._MODEL_LIST_ENDPOINTS:
330
+ try:
331
+ response = self._make_request("GET", endpoint)
332
+ except (requests.RequestException, RuntimeError) as e:
333
+ last_error = e
334
+ continue
335
+
336
+ payload = response.json() if response.text else {}
337
+ return self._normalize_model_list(
338
+ payload,
339
+ is_openai_source=("openai/v1/models" in endpoint),
340
+ )
341
+
342
+ raise RuntimeError(f"Failed to list available language models: {last_error}")
343
+
344
+ def _normalize_model_list(
345
+ self, payload: Union[Dict[str, Any], List[Any]], is_openai_source: bool = False
346
+ ) -> List[Dict[str, Any]]:
347
+ """Normalize varied model list payloads into a stable CLI schema."""
348
+ raw_models: List[Any] = []
349
+ if isinstance(payload, dict):
350
+ if "data" in payload:
351
+ data = payload.get("data")
352
+ if isinstance(data, list):
353
+ raw_models = data
354
+ elif "models" in payload:
355
+ models = payload.get("models")
356
+ if isinstance(models, list):
357
+ raw_models = models
358
+ elif isinstance(payload, list):
359
+ raw_models = payload
360
+
361
+ normalized: List[Dict[str, Any]] = []
362
+ for model in raw_models:
363
+ if not isinstance(model, dict):
364
+ continue
365
+
366
+ model_id = (
367
+ model.get("rid")
368
+ or model.get("modelRid")
369
+ or model.get("id")
370
+ or model.get("apiName")
371
+ or model.get("name")
372
+ )
373
+ if not model_id:
374
+ continue
375
+
376
+ status = (
377
+ model.get("status")
378
+ or model.get("enrollmentStatus")
379
+ or ("AVAILABLE" if is_openai_source else "UNKNOWN")
380
+ )
381
+
382
+ model_type = (
383
+ model.get("type")
384
+ or model.get("provider")
385
+ or model.get("modelType")
386
+ or model.get("family")
387
+ or ("OPENAI" if is_openai_source else "UNKNOWN")
388
+ )
389
+
390
+ normalized.append(
391
+ {
392
+ "model_rid": str(model_id),
393
+ "status": str(status),
394
+ "type": str(model_type),
395
+ "display_name": (
396
+ model.get("displayName")
397
+ or model.get("name")
398
+ or model.get("id")
399
+ or str(model_id)
400
+ ),
401
+ }
402
+ )
403
+
404
+ normalized.sort(key=lambda item: item["model_rid"])
405
+ return normalized
406
+
407
+ def get_model_enrollment_status(self, model_id: str) -> Dict[str, Any]:
408
+ """
409
+ Get enrollment status for a language model.
410
+
411
+ Args:
412
+ model_id: Model RID or API name
413
+
414
+ Returns:
415
+ Dictionary with normalized keys:
416
+ - model_rid
417
+ - status
418
+ - type
419
+ - display_name
420
+ """
421
+ encoded_model_id = quote(model_id, safe="")
422
+ last_error: Optional[Exception] = None
423
+
424
+ for endpoint_template in self._MODEL_STATUS_ENDPOINTS:
425
+ endpoint = endpoint_template.format(model_id=encoded_model_id)
426
+ try:
427
+ response = self._make_request("GET", endpoint)
428
+ payload = response.json() if response.text else {}
429
+ return self._normalize_single_model_status(model_id, payload)
430
+ except (
431
+ requests.RequestException,
432
+ RuntimeError,
433
+ json.JSONDecodeError,
434
+ ) as e:
435
+ last_error = e
436
+ continue
437
+
438
+ # Fallback: infer availability from provider-compatible OpenAI models list.
439
+ try:
440
+ response = self._make_request("GET", "/api/v2/llm/proxy/openai/v1/models")
441
+ payload = response.json() if response.text else {}
442
+ except (
443
+ requests.RequestException,
444
+ RuntimeError,
445
+ json.JSONDecodeError,
446
+ ) as fallback_error:
447
+ raise RuntimeError(
448
+ f"Failed to get model enrollment status for {model_id}. "
449
+ f"Primary endpoint error: {last_error}. "
450
+ f"Fallback endpoint error: {fallback_error}"
451
+ )
452
+
453
+ for item in payload.get("data", []) if isinstance(payload, dict) else []:
454
+ if isinstance(item, dict) and item.get("id") == model_id:
455
+ return self._normalize_single_model_status(
456
+ model_id,
457
+ item,
458
+ default_status="AVAILABLE_VIA_PROXY",
459
+ default_type="OPENAI",
460
+ default_display_name=model_id,
461
+ )
462
+
463
+ raise RuntimeError(
464
+ f"Model '{model_id}' not found via any known endpoint. "
465
+ f"Last primary endpoint error: {last_error}"
466
+ )
467
+
468
+ def enroll_model(self, model_id: str) -> Dict[str, Any]:
469
+ """
470
+ Enroll or enable a language model for API usage.
471
+
472
+ Args:
473
+ model_id: Model RID or API name
474
+
475
+ Returns:
476
+ Enrollment result dictionary with normalized keys:
477
+ - model_rid
478
+ - status
479
+ - type
480
+ - display_name
481
+ """
482
+ encoded_model_id = quote(model_id, safe="")
483
+ last_error: Optional[Exception] = None
484
+
485
+ for endpoint_template in self._MODEL_ENROLL_ENDPOINTS:
486
+ endpoint = endpoint_template.format(model_id=encoded_model_id)
487
+ try:
488
+ response = self._make_request("POST", endpoint, json_data={})
489
+ payload = response.json() if response.text else {}
490
+ return self._normalize_single_model_status(
491
+ model_id, payload, default_status="ENROLLED"
492
+ )
493
+ except (
494
+ requests.RequestException,
495
+ RuntimeError,
496
+ json.JSONDecodeError,
497
+ ) as e:
498
+ last_error = e
499
+ continue
500
+
501
+ raise RuntimeError(
502
+ f"Failed to enroll model {model_id}. "
503
+ f"Enrollment may require Model Catalog UI access for this model: {last_error}"
504
+ )
505
+
506
+ def _normalize_single_model_status(
507
+ self,
508
+ model_id: str,
509
+ payload: Dict[str, Any],
510
+ default_status: str = "UNKNOWN",
511
+ default_type: str = "UNKNOWN",
512
+ default_display_name: Optional[str] = None,
513
+ ) -> Dict[str, Any]:
514
+ """Normalize status payloads from varying endpoints."""
515
+ model_data = payload.get("data") if isinstance(payload, dict) else None
516
+ if isinstance(model_data, dict):
517
+ payload = model_data
518
+
519
+ if not isinstance(payload, dict):
520
+ payload = {}
521
+
522
+ return {
523
+ "model_rid": str(
524
+ payload.get("rid")
525
+ or payload.get("modelRid")
526
+ or payload.get("id")
527
+ or model_id
528
+ ),
529
+ "status": str(
530
+ payload.get("status")
531
+ or payload.get("enrollmentStatus")
532
+ or default_status
533
+ ),
534
+ "type": str(
535
+ payload.get("type")
536
+ or payload.get("provider")
537
+ or payload.get("modelType")
538
+ or default_type
539
+ ),
540
+ "display_name": str(
541
+ payload.get("displayName")
542
+ or payload.get("name")
543
+ or payload.get("id")
544
+ or default_display_name
545
+ or model_id
546
+ ),
547
+ }