robosystems-client 0.2.15__py3-none-any.whl → 0.2.16__py3-none-any.whl
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.
Potentially problematic release.
This version of robosystems-client might be problematic. Click here for more details.
- robosystems_client/api/agent/auto_select_agent.py +23 -27
- robosystems_client/api/agent/batch_process_queries.py +13 -13
- robosystems_client/api/agent/execute_specific_agent.py +23 -27
- robosystems_client/api/agent/get_agent_metadata.py +13 -13
- robosystems_client/api/agent/list_agents.py +23 -23
- robosystems_client/api/agent/recommend_agent.py +13 -13
- robosystems_client/api/auth/check_password_strength.py +17 -17
- robosystems_client/api/auth/complete_sso_auth.py +17 -17
- robosystems_client/api/auth/forgot_password.py +17 -23
- robosystems_client/api/auth/generate_sso_token.py +26 -26
- robosystems_client/api/auth/get_captcha_config.py +6 -6
- robosystems_client/api/auth/get_current_auth_user.py +17 -17
- robosystems_client/api/auth/get_password_policy.py +10 -10
- robosystems_client/api/auth/login_user.py +17 -17
- robosystems_client/api/auth/logout_user.py +10 -10
- robosystems_client/api/auth/refresh_auth_session.py +17 -17
- robosystems_client/api/auth/register_user.py +17 -17
- robosystems_client/api/auth/resend_verification_email.py +17 -29
- robosystems_client/api/auth/reset_password.py +17 -17
- robosystems_client/api/auth/sso_token_exchange.py +17 -17
- robosystems_client/api/auth/validate_reset_token.py +17 -17
- robosystems_client/api/auth/verify_email.py +17 -17
- robosystems_client/api/backup/create_backup.py +13 -13
- robosystems_client/api/backup/get_backup_download_url.py +22 -22
- robosystems_client/api/backup/get_backup_stats.py +13 -13
- robosystems_client/api/backup/list_backups.py +31 -31
- robosystems_client/api/backup/restore_backup.py +13 -13
- robosystems_client/api/billing/cancel_org_subscription.py +13 -13
- robosystems_client/api/billing/create_checkout_session.py +13 -13
- robosystems_client/api/billing/create_portal_session.py +13 -13
- robosystems_client/api/billing/get_checkout_status.py +13 -13
- robosystems_client/api/billing/get_org_billing_customer.py +13 -13
- robosystems_client/api/billing/get_org_subscription.py +13 -13
- robosystems_client/api/billing/get_org_upcoming_invoice.py +16 -16
- robosystems_client/api/billing/list_org_invoices.py +22 -22
- robosystems_client/api/billing/list_org_subscriptions.py +13 -13
- robosystems_client/api/connections/create_connection.py +13 -13
- robosystems_client/api/connections/create_link_token.py +13 -13
- robosystems_client/api/connections/delete_connection.py +13 -13
- robosystems_client/api/connections/exchange_link_token.py +13 -13
- robosystems_client/api/connections/get_connection.py +13 -13
- robosystems_client/api/connections/get_connection_options.py +13 -13
- robosystems_client/api/connections/init_o_auth.py +13 -13
- robosystems_client/api/connections/list_connections.py +33 -33
- robosystems_client/api/connections/oauth_callback.py +13 -13
- robosystems_client/api/connections/sync_connection.py +13 -19
- robosystems_client/api/credits_/check_credit_balance.py +38 -45
- robosystems_client/api/credits_/check_storage_limits.py +13 -13
- robosystems_client/api/credits_/get_credit_summary.py +13 -13
- robosystems_client/api/credits_/get_storage_usage.py +28 -28
- robosystems_client/api/credits_/list_credit_transactions.py +71 -71
- robosystems_client/api/files/__init__.py +1 -0
- robosystems_client/api/files/create_file_upload.py +311 -0
- robosystems_client/api/files/delete_file.py +354 -0
- robosystems_client/api/files/get_file.py +329 -0
- robosystems_client/api/files/list_files.py +346 -0
- robosystems_client/api/files/update_file.py +320 -0
- robosystems_client/api/graph_health/get_database_health.py +13 -13
- robosystems_client/api/graph_info/get_database_info.py +13 -13
- robosystems_client/api/graph_limits/get_graph_limits.py +13 -13
- robosystems_client/api/graphs/create_graph.py +13 -13
- robosystems_client/api/graphs/get_available_extensions.py +13 -13
- robosystems_client/api/graphs/get_available_graph_tiers.py +22 -22
- robosystems_client/api/graphs/get_graphs.py +13 -13
- robosystems_client/api/graphs/select_graph.py +13 -13
- robosystems_client/api/materialization/__init__.py +1 -0
- robosystems_client/api/materialization/get_materialization_status.py +272 -0
- robosystems_client/api/materialization/materialize_graph.py +416 -0
- robosystems_client/api/mcp/call_mcp_tool.py +32 -32
- robosystems_client/api/mcp/list_mcp_tools.py +13 -13
- robosystems_client/api/operations/cancel_operation.py +13 -13
- robosystems_client/api/operations/get_operation_status.py +13 -25
- robosystems_client/api/operations/stream_operation_events.py +41 -41
- robosystems_client/api/org/create_org.py +13 -13
- robosystems_client/api/org/get_org.py +13 -13
- robosystems_client/api/org/list_org_graphs.py +13 -13
- robosystems_client/api/org/list_user_orgs.py +6 -6
- robosystems_client/api/org/update_org.py +13 -13
- robosystems_client/api/org_members/invite_org_member.py +13 -13
- robosystems_client/api/org_members/list_org_members.py +13 -13
- robosystems_client/api/org_members/remove_org_member.py +13 -13
- robosystems_client/api/org_members/update_org_member_role.py +13 -13
- robosystems_client/api/org_usage/get_org_limits.py +13 -13
- robosystems_client/api/org_usage/get_org_usage.py +22 -22
- robosystems_client/api/query/execute_cypher_query.py +74 -62
- robosystems_client/api/schema/export_graph_schema.py +35 -35
- robosystems_client/api/schema/get_graph_schema.py +13 -13
- robosystems_client/api/schema/validate_schema.py +13 -13
- robosystems_client/api/service_offerings/get_service_offerings.py +17 -17
- robosystems_client/api/status/get_service_status.py +10 -10
- robosystems_client/api/subgraphs/create_subgraph.py +64 -28
- robosystems_client/api/subgraphs/delete_subgraph.py +13 -13
- robosystems_client/api/subgraphs/get_subgraph_info.py +13 -13
- robosystems_client/api/subgraphs/get_subgraph_quota.py +13 -13
- robosystems_client/api/subgraphs/list_subgraphs.py +13 -13
- robosystems_client/api/subscriptions/create_repository_subscription.py +13 -13
- robosystems_client/api/subscriptions/get_graph_subscription.py +13 -13
- robosystems_client/api/subscriptions/upgrade_subscription.py +13 -13
- robosystems_client/api/tables/list_tables.py +13 -13
- robosystems_client/api/tables/query_tables.py +13 -13
- robosystems_client/api/usage/get_graph_metrics.py +13 -13
- robosystems_client/api/usage/get_graph_usage_analytics.py +66 -66
- robosystems_client/api/user/create_user_api_key.py +13 -13
- robosystems_client/api/user/get_current_user.py +6 -6
- robosystems_client/api/user/list_user_api_keys.py +6 -6
- robosystems_client/api/user/revoke_user_api_key.py +13 -13
- robosystems_client/api/user/update_user.py +13 -13
- robosystems_client/api/user/update_user_api_key.py +13 -13
- robosystems_client/api/user/update_user_password.py +13 -13
- robosystems_client/api/views/__init__.py +1 -0
- robosystems_client/api/views/create_view.py +245 -0
- robosystems_client/api/views/save_view.py +299 -0
- robosystems_client/client.py +13 -13
- robosystems_client/extensions/__init__.py +39 -0
- robosystems_client/extensions/element_mapping_client.py +585 -0
- robosystems_client/extensions/subgraph_workspace_client.py +744 -0
- robosystems_client/extensions/table_ingest_client.py +24 -23
- robosystems_client/extensions/view_builder_client.py +617 -0
- robosystems_client/models/__init__.py +34 -10
- robosystems_client/models/account_info.py +2 -0
- robosystems_client/models/agent_list_response.py +3 -1
- robosystems_client/models/agent_list_response_agents.py +5 -3
- robosystems_client/models/agent_list_response_agents_additional_property.py +2 -0
- robosystems_client/models/agent_message.py +9 -7
- robosystems_client/models/agent_metadata_response.py +11 -9
- robosystems_client/models/agent_recommendation.py +8 -6
- robosystems_client/models/agent_recommendation_request.py +9 -7
- robosystems_client/models/agent_recommendation_request_context_type_0.py +2 -0
- robosystems_client/models/agent_recommendation_response.py +4 -2
- robosystems_client/models/agent_request.py +41 -40
- robosystems_client/models/agent_request_context_type_0.py +2 -0
- robosystems_client/models/agent_response.py +42 -44
- robosystems_client/models/agent_response_error_details_type_0.py +2 -0
- robosystems_client/models/agent_response_metadata_type_0.py +2 -0
- robosystems_client/models/agent_response_tokens_used_type_0.py +2 -0
- robosystems_client/models/api_key_info.py +18 -16
- robosystems_client/models/api_keys_response.py +4 -2
- robosystems_client/models/auth_response.py +26 -24
- robosystems_client/models/auth_response_org_type_0.py +2 -0
- robosystems_client/models/auth_response_user.py +2 -0
- robosystems_client/models/available_extension.py +5 -3
- robosystems_client/models/available_extensions_response.py +4 -2
- robosystems_client/models/available_graph_tiers_response.py +4 -2
- robosystems_client/models/backup_create_request.py +18 -16
- robosystems_client/models/backup_download_url_response.py +2 -0
- robosystems_client/models/backup_limits.py +2 -0
- robosystems_client/models/backup_list_response.py +4 -2
- robosystems_client/models/backup_response.py +13 -11
- robosystems_client/models/backup_restore_request.py +7 -6
- robosystems_client/models/backup_stats_response.py +9 -7
- robosystems_client/models/backup_stats_response_backup_formats.py +2 -0
- robosystems_client/models/batch_agent_request.py +7 -5
- robosystems_client/models/batch_agent_response.py +4 -2
- robosystems_client/models/billing_customer.py +10 -8
- robosystems_client/models/cancel_operation_response_canceloperation.py +2 -0
- robosystems_client/models/check_credit_balance_response_checkcreditbalance.py +2 -0
- robosystems_client/models/checkout_response.py +22 -20
- robosystems_client/models/checkout_status_response.py +19 -17
- robosystems_client/models/connection_options_response.py +4 -2
- robosystems_client/models/connection_provider_info.py +26 -24
- robosystems_client/models/connection_response.py +14 -12
- robosystems_client/models/connection_response_metadata.py +2 -0
- robosystems_client/models/copy_operation_limits.py +2 -0
- robosystems_client/models/create_api_key_request.py +13 -11
- robosystems_client/models/create_api_key_response.py +3 -1
- robosystems_client/models/create_checkout_request.py +3 -1
- robosystems_client/models/create_checkout_request_resource_config.py +2 -0
- robosystems_client/models/create_connection_request.py +21 -21
- robosystems_client/models/create_graph_request.py +25 -25
- robosystems_client/models/create_org_request.py +7 -5
- robosystems_client/models/create_repository_subscription_request.py +2 -0
- robosystems_client/models/create_subgraph_request.py +30 -19
- robosystems_client/models/create_subgraph_request_metadata_type_0.py +2 -0
- robosystems_client/models/create_view_request.py +141 -0
- robosystems_client/models/credit_limits.py +2 -0
- robosystems_client/models/credit_summary.py +3 -1
- robosystems_client/models/credit_summary_operation_breakdown.py +2 -0
- robosystems_client/models/credit_summary_response.py +8 -6
- robosystems_client/models/custom_schema_definition.py +39 -33
- robosystems_client/models/custom_schema_definition_metadata.py +2 -0
- robosystems_client/models/custom_schema_definition_nodes_item.py +2 -0
- robosystems_client/models/custom_schema_definition_relationships_item.py +2 -0
- robosystems_client/models/cypher_query_request.py +15 -13
- robosystems_client/models/cypher_query_request_parameters_type_0.py +2 -0
- robosystems_client/models/database_health_response.py +21 -19
- robosystems_client/models/database_info_response.py +13 -11
- robosystems_client/models/delete_file_response.py +54 -1
- robosystems_client/models/delete_subgraph_request.py +12 -10
- robosystems_client/models/delete_subgraph_response.py +13 -11
- robosystems_client/models/detailed_transactions_response.py +6 -4
- robosystems_client/models/detailed_transactions_response_date_range.py +2 -0
- robosystems_client/models/detailed_transactions_response_summary.py +5 -3
- robosystems_client/models/email_verification_request.py +2 -0
- robosystems_client/models/enhanced_credit_transaction_response.py +24 -22
- robosystems_client/models/enhanced_credit_transaction_response_metadata.py +2 -0
- robosystems_client/models/enhanced_file_status_layers.py +83 -0
- robosystems_client/models/error_response.py +19 -17
- robosystems_client/models/exchange_token_request.py +9 -7
- robosystems_client/models/exchange_token_request_metadata_type_0.py +2 -0
- robosystems_client/models/execute_cypher_query_response_200.py +25 -21
- robosystems_client/models/execute_cypher_query_response_200_data_item.py +2 -0
- robosystems_client/models/fact_detail.py +109 -0
- robosystems_client/models/file_info.py +18 -16
- robosystems_client/models/file_layer_status.py +123 -0
- robosystems_client/models/file_status_update.py +14 -0
- robosystems_client/models/file_upload_request.py +25 -3
- robosystems_client/models/file_upload_response.py +2 -0
- robosystems_client/models/forgot_password_request.py +2 -0
- robosystems_client/models/forgot_password_response_forgotpassword.py +2 -0
- robosystems_client/models/get_current_auth_user_response_getcurrentauthuser.py +2 -0
- robosystems_client/models/get_file_info_response.py +62 -21
- robosystems_client/models/get_operation_status_response_getoperationstatus.py +2 -0
- robosystems_client/models/get_storage_usage_response_getstorageusage.py +2 -0
- robosystems_client/models/graph_info.py +10 -8
- robosystems_client/models/graph_limits_response.py +14 -12
- robosystems_client/models/graph_metadata.py +14 -12
- robosystems_client/models/graph_metrics_response.py +17 -15
- robosystems_client/models/graph_metrics_response_estimated_size.py +2 -0
- robosystems_client/models/graph_metrics_response_health_status.py +2 -0
- robosystems_client/models/graph_metrics_response_node_counts.py +2 -0
- robosystems_client/models/graph_metrics_response_relationship_counts.py +2 -0
- robosystems_client/models/graph_subscription_response.py +28 -26
- robosystems_client/models/graph_subscription_tier.py +15 -13
- robosystems_client/models/graph_subscriptions.py +5 -3
- robosystems_client/models/graph_tier_backup.py +2 -0
- robosystems_client/models/graph_tier_copy_operations.py +2 -0
- robosystems_client/models/graph_tier_info.py +15 -13
- robosystems_client/models/graph_tier_instance.py +2 -0
- robosystems_client/models/graph_tier_limits.py +10 -8
- robosystems_client/models/graph_usage_response.py +32 -30
- robosystems_client/models/graph_usage_response_recent_events_item.py +2 -0
- robosystems_client/models/health_status.py +9 -7
- robosystems_client/models/health_status_details_type_0.py +2 -0
- robosystems_client/models/http_validation_error.py +12 -8
- robosystems_client/models/initial_entity_data.py +38 -36
- robosystems_client/models/invite_member_request.py +9 -7
- robosystems_client/models/invoice.py +35 -33
- robosystems_client/models/invoice_line_item.py +13 -11
- robosystems_client/models/invoices_response.py +4 -2
- robosystems_client/models/link_token_request.py +21 -23
- robosystems_client/models/link_token_request_options_type_0.py +2 -0
- robosystems_client/models/list_org_graphs_response_200_item.py +2 -0
- robosystems_client/models/list_subgraphs_response.py +15 -13
- robosystems_client/models/list_table_files_response.py +27 -11
- robosystems_client/models/login_request.py +2 -0
- robosystems_client/models/logout_user_response_logoutuser.py +2 -0
- robosystems_client/models/{bulk_ingest_request.py → materialize_request.py} +25 -15
- robosystems_client/models/materialize_response.py +131 -0
- robosystems_client/models/materialize_status_response.py +172 -0
- robosystems_client/models/mcp_tool_call.py +7 -5
- robosystems_client/models/mcp_tool_call_arguments.py +2 -0
- robosystems_client/models/mcp_tools_response.py +4 -2
- robosystems_client/models/mcp_tools_response_tools_item.py +2 -0
- robosystems_client/models/o_auth_callback_request.py +18 -16
- robosystems_client/models/o_auth_init_request.py +14 -12
- robosystems_client/models/o_auth_init_request_additional_params_type_0.py +2 -0
- robosystems_client/models/o_auth_init_response.py +2 -0
- robosystems_client/models/offering_repository_plan.py +9 -7
- robosystems_client/models/offering_repository_plan_rate_limits_type_0.py +8 -8
- robosystems_client/models/operation_costs.py +4 -2
- robosystems_client/models/operation_costs_ai_operations.py +2 -0
- robosystems_client/models/operation_costs_token_pricing.py +5 -3
- robosystems_client/models/org_detail_response.py +13 -11
- robosystems_client/models/org_detail_response_graphs_item.py +2 -0
- robosystems_client/models/org_detail_response_limits_type_0.py +2 -0
- robosystems_client/models/org_detail_response_members_item.py +2 -0
- robosystems_client/models/org_limits_response.py +3 -1
- robosystems_client/models/org_limits_response_current_usage.py +2 -0
- robosystems_client/models/org_list_response.py +4 -2
- robosystems_client/models/org_member_list_response.py +4 -2
- robosystems_client/models/org_member_response.py +2 -0
- robosystems_client/models/org_response.py +2 -0
- robosystems_client/models/org_usage_response.py +7 -5
- robosystems_client/models/org_usage_response_daily_trend_item.py +2 -0
- robosystems_client/models/org_usage_response_graph_details_item.py +2 -0
- robosystems_client/models/org_usage_summary.py +18 -16
- robosystems_client/models/password_check_request.py +8 -6
- robosystems_client/models/password_check_response.py +3 -1
- robosystems_client/models/password_check_response_character_types.py +2 -0
- robosystems_client/models/password_policy_response.py +3 -1
- robosystems_client/models/password_policy_response_policy.py +2 -0
- robosystems_client/models/payment_method.py +23 -21
- robosystems_client/models/performance_insights.py +5 -3
- robosystems_client/models/performance_insights_operation_stats.py +2 -0
- robosystems_client/models/performance_insights_slow_queries_item.py +2 -0
- robosystems_client/models/plaid_connection_config.py +30 -30
- robosystems_client/models/plaid_connection_config_accounts_type_0_item.py +2 -0
- robosystems_client/models/plaid_connection_config_institution_type_0.py +2 -0
- robosystems_client/models/portal_session_response.py +2 -0
- robosystems_client/models/query_limits.py +2 -0
- robosystems_client/models/quick_books_connection_config.py +13 -11
- robosystems_client/models/rate_limits.py +2 -0
- robosystems_client/models/register_request.py +8 -6
- robosystems_client/models/repository_info.py +4 -2
- robosystems_client/models/repository_subscriptions.py +4 -2
- robosystems_client/models/resend_verification_email_response_resendverificationemail.py +2 -0
- robosystems_client/models/reset_password_request.py +2 -0
- robosystems_client/models/reset_password_validate_response.py +8 -6
- robosystems_client/models/save_view_request.py +138 -0
- robosystems_client/models/save_view_response.py +181 -0
- robosystems_client/models/schema_export_response.py +16 -14
- robosystems_client/models/schema_export_response_data_stats_type_0.py +2 -0
- robosystems_client/models/schema_export_response_schema_definition_type_0.py +2 -0
- robosystems_client/models/schema_info_response.py +3 -1
- robosystems_client/models/schema_info_response_schema.py +2 -0
- robosystems_client/models/schema_validation_request.py +18 -17
- robosystems_client/models/schema_validation_request_schema_definition_type_0.py +2 -0
- robosystems_client/models/schema_validation_response.py +28 -33
- robosystems_client/models/schema_validation_response_compatibility_type_0.py +2 -0
- robosystems_client/models/schema_validation_response_stats_type_0.py +2 -0
- robosystems_client/models/sec_connection_config.py +8 -6
- robosystems_client/models/selection_criteria.py +19 -17
- robosystems_client/models/service_offering_summary.py +2 -0
- robosystems_client/models/service_offerings_response.py +6 -4
- robosystems_client/models/sso_complete_request.py +2 -0
- robosystems_client/models/sso_exchange_request.py +8 -6
- robosystems_client/models/sso_exchange_response.py +2 -0
- robosystems_client/models/sso_token_response.py +2 -0
- robosystems_client/models/storage_info.py +4 -2
- robosystems_client/models/storage_info_included_per_tier.py +2 -0
- robosystems_client/models/storage_info_overage_pricing.py +2 -0
- robosystems_client/models/storage_limit_response.py +9 -7
- robosystems_client/models/storage_limits.py +8 -6
- robosystems_client/models/storage_summary.py +2 -0
- robosystems_client/models/structure_detail.py +85 -0
- robosystems_client/models/subgraph_quota_response.py +23 -21
- robosystems_client/models/subgraph_response.py +35 -35
- robosystems_client/models/subgraph_response_metadata_type_0.py +2 -0
- robosystems_client/models/subgraph_summary.py +14 -12
- robosystems_client/models/success_response.py +11 -9
- robosystems_client/models/success_response_data_type_0.py +2 -0
- robosystems_client/models/sync_connection_request.py +11 -9
- robosystems_client/models/sync_connection_request_sync_options_type_0.py +2 -0
- robosystems_client/models/sync_connection_response_syncconnection.py +2 -0
- robosystems_client/models/table_info.py +12 -10
- robosystems_client/models/table_list_response.py +4 -2
- robosystems_client/models/table_query_request.py +9 -7
- robosystems_client/models/table_query_response.py +2 -0
- robosystems_client/models/token_pricing.py +2 -0
- robosystems_client/models/transaction_summary_response.py +13 -11
- robosystems_client/models/upcoming_invoice.py +10 -8
- robosystems_client/models/update_api_key_request.py +13 -11
- robosystems_client/models/{update_file_status_response_updatefilestatus.py → update_file_response_updatefile.py} +7 -5
- robosystems_client/models/update_member_role_request.py +2 -0
- robosystems_client/models/update_org_request.py +14 -12
- robosystems_client/models/update_password_request.py +2 -0
- robosystems_client/models/update_user_request.py +13 -11
- robosystems_client/models/upgrade_subscription_request.py +2 -0
- robosystems_client/models/user_graphs_response.py +10 -8
- robosystems_client/models/user_response.py +22 -18
- robosystems_client/models/validation_error.py +8 -6
- robosystems_client/models/view_axis_config.py +276 -0
- robosystems_client/models/view_axis_config_element_labels_type_0.py +46 -0
- robosystems_client/models/view_axis_config_member_labels_type_0.py +46 -0
- robosystems_client/models/view_config.py +127 -0
- robosystems_client/models/view_source.py +144 -0
- robosystems_client/models/view_source_type.py +9 -0
- robosystems_client/types.py +9 -9
- {robosystems_client-0.2.15.dist-info → robosystems_client-0.2.16.dist-info}/METADATA +1 -1
- robosystems_client-0.2.16.dist-info/RECORD +418 -0
- robosystems_client/api/tables/delete_file.py +0 -317
- robosystems_client/api/tables/get_file_info.py +0 -249
- robosystems_client/api/tables/get_upload_url.py +0 -380
- robosystems_client/api/tables/ingest_tables.py +0 -456
- robosystems_client/api/tables/list_table_files.py +0 -329
- robosystems_client/api/tables/update_file_status.py +0 -395
- robosystems_client/models/bulk_ingest_response.py +0 -137
- robosystems_client/models/table_ingest_result.py +0 -107
- robosystems_client-0.2.15.dist-info/RECORD +0 -396
- {robosystems_client-0.2.15.dist-info → robosystems_client-0.2.16.dist-info}/WHEEL +0 -0
- {robosystems_client-0.2.15.dist-info → robosystems_client-0.2.16.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
"""Subgraph Workspace Client Extension
|
|
2
|
+
|
|
3
|
+
Client for managing subgraph workspaces following the Financial Report Creator architecture.
|
|
4
|
+
Supports creating isolated workspaces, transferring data, and publishing to main graph.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional, Dict, Any
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import Enum
|
|
10
|
+
import json
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StorageType(Enum):
|
|
15
|
+
"""Subgraph storage types"""
|
|
16
|
+
|
|
17
|
+
IN_MEMORY = "in_memory"
|
|
18
|
+
DISK_BASED = "disk_based"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class SubgraphWorkspace:
|
|
23
|
+
"""Represents a subgraph workspace"""
|
|
24
|
+
|
|
25
|
+
graph_id: str # Full subgraph ID (e.g., kg123_workspace)
|
|
26
|
+
parent_id: str # Parent graph ID (e.g., kg123)
|
|
27
|
+
name: str # Workspace name (e.g., workspace)
|
|
28
|
+
display_name: str
|
|
29
|
+
storage_type: StorageType
|
|
30
|
+
created_at: str
|
|
31
|
+
status: str = "active"
|
|
32
|
+
fork_status: Optional[Dict[str, Any]] = None # Fork operation status if forked
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ExportResult:
|
|
37
|
+
"""Result from exporting subgraph to parquet"""
|
|
38
|
+
|
|
39
|
+
shared_filename: str
|
|
40
|
+
files_exported: List[Dict[str, Any]]
|
|
41
|
+
total_rows: int
|
|
42
|
+
execution_time_ms: int
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class PublishResult:
|
|
47
|
+
"""Result from publishing subgraph to main graph"""
|
|
48
|
+
|
|
49
|
+
nodes_created: int
|
|
50
|
+
relationships_created: int
|
|
51
|
+
execution_time_ms: int
|
|
52
|
+
success: bool
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SubgraphWorkspaceClient:
|
|
56
|
+
"""
|
|
57
|
+
Client for managing subgraph workspaces.
|
|
58
|
+
|
|
59
|
+
Provides functionality to:
|
|
60
|
+
- Create/delete subgraph workspaces
|
|
61
|
+
- Transfer data from main graph to subgraph
|
|
62
|
+
- Export subgraph to parquet files
|
|
63
|
+
- Publish subgraph to main graph via incremental ingestion
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, api_client, query_client=None):
|
|
67
|
+
"""
|
|
68
|
+
Initialize with API client and optional query client.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
api_client: RoboSystems API client for subgraph operations
|
|
72
|
+
query_client: Optional query client for executing Cypher
|
|
73
|
+
"""
|
|
74
|
+
self.api = api_client
|
|
75
|
+
self.query = query_client
|
|
76
|
+
|
|
77
|
+
async def create_workspace(
|
|
78
|
+
self,
|
|
79
|
+
parent_graph_id: str,
|
|
80
|
+
name: str,
|
|
81
|
+
display_name: str = None,
|
|
82
|
+
storage_type: StorageType = StorageType.IN_MEMORY,
|
|
83
|
+
schema_extensions: List[str] = None,
|
|
84
|
+
fork_parent: bool = False,
|
|
85
|
+
) -> SubgraphWorkspace:
|
|
86
|
+
"""
|
|
87
|
+
Create a new subgraph workspace.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
parent_graph_id: Parent graph ID (e.g., kg123)
|
|
91
|
+
name: Workspace name (alphanumeric, 1-20 chars)
|
|
92
|
+
display_name: Optional human-readable name
|
|
93
|
+
storage_type: IN_MEMORY (fast, <10K nodes) or DISK_BASED (persistent, >100K nodes)
|
|
94
|
+
schema_extensions: List of schema extensions (e.g., ["roboledger"])
|
|
95
|
+
fork_parent: If True, copy all data from parent graph (creates a "fork")
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
SubgraphWorkspace object
|
|
99
|
+
"""
|
|
100
|
+
# Construct request for subgraph creation
|
|
101
|
+
request_body = {
|
|
102
|
+
"name": name,
|
|
103
|
+
"display_name": display_name or f"Workspace {name}",
|
|
104
|
+
"storage_type": storage_type.value,
|
|
105
|
+
"schema_extensions": schema_extensions or ["roboledger"],
|
|
106
|
+
"fork_parent": fork_parent, # Pass fork flag to server
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Use httpx to call API
|
|
110
|
+
async with httpx.AsyncClient() as client:
|
|
111
|
+
headers = {"X-API-Key": self.api.token, "Content-Type": "application/json"}
|
|
112
|
+
response = await client.post(
|
|
113
|
+
f"{self.api._base_url}/v1/graphs/{parent_graph_id}/subgraphs",
|
|
114
|
+
json=request_body,
|
|
115
|
+
headers=headers,
|
|
116
|
+
)
|
|
117
|
+
result = response.json()
|
|
118
|
+
|
|
119
|
+
# Construct full subgraph ID
|
|
120
|
+
subgraph_id = f"{parent_graph_id}_{name}"
|
|
121
|
+
|
|
122
|
+
workspace = SubgraphWorkspace(
|
|
123
|
+
graph_id=subgraph_id,
|
|
124
|
+
parent_id=parent_graph_id,
|
|
125
|
+
name=name,
|
|
126
|
+
display_name=result.get("display_name", display_name),
|
|
127
|
+
storage_type=storage_type,
|
|
128
|
+
created_at=result.get("created_at"),
|
|
129
|
+
status="active",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# If fork_parent is True, trigger server-side fork
|
|
133
|
+
# The server will handle the fork operation with progress monitoring
|
|
134
|
+
if fork_parent:
|
|
135
|
+
# Fork happens server-side during creation when fork_parent=True
|
|
136
|
+
# For client-side monitoring, use fork_from_parent_with_sse() method
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
return workspace
|
|
140
|
+
|
|
141
|
+
async def create_workspace_with_fork(
|
|
142
|
+
self,
|
|
143
|
+
parent_graph_id: str,
|
|
144
|
+
name: str,
|
|
145
|
+
display_name: str = None,
|
|
146
|
+
fork_parent: bool = True,
|
|
147
|
+
fork_options: Dict[str, Any] = None,
|
|
148
|
+
progress_callback: Optional[callable] = None,
|
|
149
|
+
) -> SubgraphWorkspace:
|
|
150
|
+
"""
|
|
151
|
+
Create a subgraph workspace with fork from parent using SSE monitoring.
|
|
152
|
+
|
|
153
|
+
This method creates a subgraph and monitors the fork operation via SSE
|
|
154
|
+
if fork_parent=True. The fork operation copies data from the parent
|
|
155
|
+
graph to the new subgraph.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
parent_graph_id: Parent graph ID
|
|
159
|
+
name: Workspace name (alphanumeric only, 1-20 chars)
|
|
160
|
+
display_name: Human-readable display name
|
|
161
|
+
fork_parent: If True, fork data from parent graph
|
|
162
|
+
fork_options: Fork options dict:
|
|
163
|
+
- tables: List of tables to copy or "all" (default: "all")
|
|
164
|
+
- exclude_patterns: List of table patterns to exclude (e.g., ["Report*"])
|
|
165
|
+
progress_callback: Optional callback(msg: str, pct: float) for progress updates
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
SubgraphWorkspace with fork_status if fork was performed
|
|
169
|
+
"""
|
|
170
|
+
# Create request body
|
|
171
|
+
request_body = {
|
|
172
|
+
"name": name,
|
|
173
|
+
"display_name": display_name or f"Workspace {name}",
|
|
174
|
+
"fork_parent": fork_parent,
|
|
175
|
+
"metadata": {"fork_options": fork_options} if fork_options else None,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Use httpx directly to call API
|
|
179
|
+
async with httpx.AsyncClient() as client:
|
|
180
|
+
headers = {"X-API-Key": self.api.token, "Content-Type": "application/json"}
|
|
181
|
+
|
|
182
|
+
# Call API to create subgraph with fork
|
|
183
|
+
response = await client.post(
|
|
184
|
+
f"{self.api._base_url}/v1/graphs/{parent_graph_id}/subgraphs",
|
|
185
|
+
json=request_body,
|
|
186
|
+
headers=headers,
|
|
187
|
+
)
|
|
188
|
+
result = response.json()
|
|
189
|
+
|
|
190
|
+
# If fork_parent=True, response includes operation_id for SSE monitoring
|
|
191
|
+
if fork_parent and "operation_id" in result:
|
|
192
|
+
operation_id = result["operation_id"]
|
|
193
|
+
|
|
194
|
+
# Monitor fork progress via SSE
|
|
195
|
+
if progress_callback:
|
|
196
|
+
# Connect to SSE endpoint
|
|
197
|
+
sse_url = f"{self.api._base_url}/v1/operations/{operation_id}/stream"
|
|
198
|
+
headers = {"X-API-Key": self.api.token}
|
|
199
|
+
|
|
200
|
+
async with httpx.AsyncClient() as client:
|
|
201
|
+
async with client.stream("GET", sse_url, headers=headers) as sse_response:
|
|
202
|
+
async for line in sse_response.aiter_lines():
|
|
203
|
+
if line.startswith("data: "):
|
|
204
|
+
try:
|
|
205
|
+
event_data = json.loads(line[6:])
|
|
206
|
+
if event_data.get("event") == "operation_progress":
|
|
207
|
+
msg = event_data.get("data", {}).get("message", "")
|
|
208
|
+
pct = event_data.get("data", {}).get("progress_percent", 0)
|
|
209
|
+
if progress_callback:
|
|
210
|
+
await progress_callback(msg, pct)
|
|
211
|
+
elif event_data.get("event") == "operation_completed":
|
|
212
|
+
if progress_callback:
|
|
213
|
+
await progress_callback("Fork completed", 100)
|
|
214
|
+
break
|
|
215
|
+
elif event_data.get("event") == "operation_error":
|
|
216
|
+
error = event_data.get("data", {}).get("error", "Unknown error")
|
|
217
|
+
if progress_callback:
|
|
218
|
+
await progress_callback(f"Fork failed: {error}", 0)
|
|
219
|
+
break
|
|
220
|
+
except json.JSONDecodeError:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
# Get final status via API
|
|
224
|
+
async with httpx.AsyncClient() as client:
|
|
225
|
+
headers = {"X-API-Key": self.api.token}
|
|
226
|
+
status_response = await client.get(
|
|
227
|
+
f"{self.api._base_url}/v1/operations/{operation_id}/status", headers=headers
|
|
228
|
+
)
|
|
229
|
+
final_status = status_response.json()
|
|
230
|
+
|
|
231
|
+
# Construct full subgraph ID
|
|
232
|
+
subgraph_id = f"{parent_graph_id}_{name}"
|
|
233
|
+
|
|
234
|
+
workspace = SubgraphWorkspace(
|
|
235
|
+
graph_id=subgraph_id,
|
|
236
|
+
parent_id=parent_graph_id,
|
|
237
|
+
name=name,
|
|
238
|
+
display_name=display_name or f"Workspace {name}",
|
|
239
|
+
storage_type=StorageType.IN_MEMORY,
|
|
240
|
+
created_at=final_status.get("created_at"),
|
|
241
|
+
status="active",
|
|
242
|
+
fork_status=final_status.get("result", {}).get("fork_status"),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return workspace
|
|
246
|
+
|
|
247
|
+
# Non-fork path (immediate response)
|
|
248
|
+
subgraph_id = f"{parent_graph_id}_{name}"
|
|
249
|
+
|
|
250
|
+
return SubgraphWorkspace(
|
|
251
|
+
graph_id=subgraph_id,
|
|
252
|
+
parent_id=parent_graph_id,
|
|
253
|
+
name=name,
|
|
254
|
+
display_name=result.get("display_name", display_name),
|
|
255
|
+
storage_type=StorageType.IN_MEMORY,
|
|
256
|
+
created_at=result.get("created_at"),
|
|
257
|
+
status="active",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
async def delete_workspace(
|
|
261
|
+
self,
|
|
262
|
+
parent_graph_id: str,
|
|
263
|
+
workspace_name: str,
|
|
264
|
+
force: bool = False,
|
|
265
|
+
create_backup: bool = False,
|
|
266
|
+
) -> bool:
|
|
267
|
+
"""
|
|
268
|
+
Delete a subgraph workspace.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
parent_graph_id: Parent graph ID
|
|
272
|
+
workspace_name: Workspace name to delete
|
|
273
|
+
force: Force deletion even if subgraph contains data
|
|
274
|
+
create_backup: Create backup before deletion
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
True if deleted successfully
|
|
278
|
+
"""
|
|
279
|
+
params = {"force": force, "create_backup": create_backup}
|
|
280
|
+
|
|
281
|
+
response = await self.api.delete(
|
|
282
|
+
f"/v1/graphs/{parent_graph_id}/subgraphs/{workspace_name}", params=params
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return response.status_code == 200
|
|
286
|
+
|
|
287
|
+
async def list_workspaces(self, parent_graph_id: str) -> List[SubgraphWorkspace]:
|
|
288
|
+
"""
|
|
289
|
+
List all subgraph workspaces for a parent graph.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
parent_graph_id: Parent graph ID
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
List of SubgraphWorkspace objects
|
|
296
|
+
"""
|
|
297
|
+
response = await self.api.get(f"/v1/graphs/{parent_graph_id}/subgraphs")
|
|
298
|
+
subgraphs = response.json()
|
|
299
|
+
|
|
300
|
+
workspaces = []
|
|
301
|
+
for subgraph in subgraphs:
|
|
302
|
+
workspaces.append(
|
|
303
|
+
SubgraphWorkspace(
|
|
304
|
+
graph_id=f"{parent_graph_id}_{subgraph['name']}",
|
|
305
|
+
parent_id=parent_graph_id,
|
|
306
|
+
name=subgraph["name"],
|
|
307
|
+
display_name=subgraph.get("display_name", ""),
|
|
308
|
+
storage_type=StorageType(subgraph.get("storage_type", "in_memory")),
|
|
309
|
+
created_at=subgraph.get("created_at", ""),
|
|
310
|
+
status=subgraph.get("status", "active"),
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
return workspaces
|
|
315
|
+
|
|
316
|
+
async def copy_data_from_main_graph(
|
|
317
|
+
self,
|
|
318
|
+
workspace_id: str,
|
|
319
|
+
parent_graph_id: str,
|
|
320
|
+
node_types: List[str],
|
|
321
|
+
filters: Dict[str, Any] = None,
|
|
322
|
+
) -> int:
|
|
323
|
+
"""
|
|
324
|
+
Copy data from main graph to subgraph workspace.
|
|
325
|
+
|
|
326
|
+
Queries nodes from main graph and creates them in subgraph.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
workspace_id: Subgraph workspace ID (e.g., kg123_workspace)
|
|
330
|
+
parent_graph_id: Parent graph ID to query from
|
|
331
|
+
node_types: List of node types to copy (e.g., ["Element", "Period", "Unit"])
|
|
332
|
+
filters: Optional filters for querying (e.g., {"period_end": "2024-12-31"})
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Number of nodes copied
|
|
336
|
+
"""
|
|
337
|
+
if not self.query:
|
|
338
|
+
raise ValueError("Query client required for data transfer")
|
|
339
|
+
|
|
340
|
+
total_copied = 0
|
|
341
|
+
|
|
342
|
+
for node_type in node_types:
|
|
343
|
+
# Build query for main graph
|
|
344
|
+
where_clause = ""
|
|
345
|
+
if filters:
|
|
346
|
+
conditions = []
|
|
347
|
+
for key, value in filters.items():
|
|
348
|
+
conditions.append(f"n.{key} = '{value}'")
|
|
349
|
+
where_clause = f"WHERE {' AND '.join(conditions)}"
|
|
350
|
+
|
|
351
|
+
# Query from main graph
|
|
352
|
+
query_cypher = f"""
|
|
353
|
+
MATCH (n:{node_type})
|
|
354
|
+
{where_clause}
|
|
355
|
+
RETURN n
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
result = await self.query.query(parent_graph_id, query_cypher)
|
|
359
|
+
|
|
360
|
+
if result and result.data:
|
|
361
|
+
# Batch create in subgraph
|
|
362
|
+
for batch in self._batch_nodes(result.data, 100):
|
|
363
|
+
create_cypher = self._build_batch_create_cypher(node_type, batch)
|
|
364
|
+
await self.query.query(workspace_id, create_cypher)
|
|
365
|
+
total_copied += len(batch)
|
|
366
|
+
|
|
367
|
+
return total_copied
|
|
368
|
+
|
|
369
|
+
async def copy_facts_with_aspects(
|
|
370
|
+
self,
|
|
371
|
+
workspace_id: str,
|
|
372
|
+
parent_graph_id: str,
|
|
373
|
+
fact_set_ids: List[str] = None,
|
|
374
|
+
period_start: str = None,
|
|
375
|
+
period_end: str = None,
|
|
376
|
+
entity_id: str = None,
|
|
377
|
+
) -> int:
|
|
378
|
+
"""
|
|
379
|
+
Copy facts with all their aspects (element, period, unit, dimensions).
|
|
380
|
+
|
|
381
|
+
This is optimized for copying complete fact data with relationships.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
workspace_id: Subgraph workspace ID
|
|
385
|
+
parent_graph_id: Parent graph ID
|
|
386
|
+
fact_set_ids: Optional list of fact set IDs to copy
|
|
387
|
+
period_start: Optional start date filter
|
|
388
|
+
period_end: Optional end date filter
|
|
389
|
+
entity_id: Optional entity filter
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Number of facts copied
|
|
393
|
+
"""
|
|
394
|
+
if not self.query:
|
|
395
|
+
raise ValueError("Query client required for fact transfer")
|
|
396
|
+
|
|
397
|
+
# Build WHERE clause
|
|
398
|
+
conditions = []
|
|
399
|
+
params = {}
|
|
400
|
+
|
|
401
|
+
if fact_set_ids:
|
|
402
|
+
conditions.append("fs.identifier IN $fact_set_ids")
|
|
403
|
+
params["fact_set_ids"] = fact_set_ids
|
|
404
|
+
|
|
405
|
+
if period_start and period_end:
|
|
406
|
+
conditions.append("p.end_date >= $period_start AND p.end_date <= $period_end")
|
|
407
|
+
params["period_start"] = period_start
|
|
408
|
+
params["period_end"] = period_end
|
|
409
|
+
|
|
410
|
+
if entity_id:
|
|
411
|
+
conditions.append("e.identifier = $entity_id")
|
|
412
|
+
params["entity_id"] = entity_id
|
|
413
|
+
|
|
414
|
+
where_clause = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
|
415
|
+
|
|
416
|
+
# Query facts with all relationships from main graph
|
|
417
|
+
query_cypher = f"""
|
|
418
|
+
MATCH (fs:FactSet)-[:FACT_SET_CONTAINS_FACT]->(f:Fact)
|
|
419
|
+
MATCH (f)-[:FACT_HAS_ELEMENT]->(el:Element)
|
|
420
|
+
MATCH (f)-[:FACT_HAS_PERIOD]->(p:Period)
|
|
421
|
+
MATCH (f)-[:FACT_HAS_ENTITY]->(e:Entity)
|
|
422
|
+
OPTIONAL MATCH (f)-[:FACT_HAS_UNIT]->(u:Unit)
|
|
423
|
+
OPTIONAL MATCH (f)-[:FACT_HAS_DIMENSION]->(d:FactDimension)
|
|
424
|
+
{where_clause}
|
|
425
|
+
RETURN f, el, p, e, u, collect(d) as dimensions
|
|
426
|
+
"""
|
|
427
|
+
|
|
428
|
+
result = await self.query.query(parent_graph_id, query_cypher, params)
|
|
429
|
+
|
|
430
|
+
if not result or not result.data:
|
|
431
|
+
return 0
|
|
432
|
+
|
|
433
|
+
# Create nodes and relationships in subgraph
|
|
434
|
+
# This would be done in batches with proper MERGE statements
|
|
435
|
+
facts_copied = 0
|
|
436
|
+
|
|
437
|
+
for row in result.data:
|
|
438
|
+
# Create fact and all related nodes in subgraph
|
|
439
|
+
create_cypher = self._build_fact_create_cypher(row)
|
|
440
|
+
await self.query.query(workspace_id, create_cypher)
|
|
441
|
+
facts_copied += 1
|
|
442
|
+
|
|
443
|
+
return facts_copied
|
|
444
|
+
|
|
445
|
+
async def export_to_parquet(
|
|
446
|
+
self, workspace_id: str, shared_filename: str, tables: List[str] = None
|
|
447
|
+
) -> ExportResult:
|
|
448
|
+
"""
|
|
449
|
+
Export subgraph to parquet files.
|
|
450
|
+
|
|
451
|
+
Each table is exported to a separate parquet file with the shared filename
|
|
452
|
+
for provenance tracking and incremental ingestion.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
workspace_id: Subgraph workspace ID
|
|
456
|
+
shared_filename: Shared filename for all tables (e.g., "report_nvda_2024q4.parquet")
|
|
457
|
+
tables: List of tables to export (default: all report tables)
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
ExportResult with file details
|
|
461
|
+
"""
|
|
462
|
+
if tables is None:
|
|
463
|
+
tables = [
|
|
464
|
+
"Report",
|
|
465
|
+
"ReportSection",
|
|
466
|
+
"FactSet",
|
|
467
|
+
"Fact",
|
|
468
|
+
"Structure",
|
|
469
|
+
"Association",
|
|
470
|
+
"Element",
|
|
471
|
+
"Period",
|
|
472
|
+
"Unit",
|
|
473
|
+
]
|
|
474
|
+
|
|
475
|
+
request_body = {
|
|
476
|
+
"shared_filename": shared_filename,
|
|
477
|
+
"export_all_tables": False,
|
|
478
|
+
"tables": tables,
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
response = await self.api.post(
|
|
482
|
+
f"/v1/graphs/{workspace_id}/export", json=request_body
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
result = response.json()
|
|
486
|
+
|
|
487
|
+
return ExportResult(
|
|
488
|
+
shared_filename=shared_filename,
|
|
489
|
+
files_exported=result.get("files_exported", []),
|
|
490
|
+
total_rows=sum(f.get("row_count", 0) for f in result.get("files_exported", [])),
|
|
491
|
+
execution_time_ms=result.get("execution_time_ms", 0),
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
async def publish_to_main_graph(
|
|
495
|
+
self,
|
|
496
|
+
workspace_id: str,
|
|
497
|
+
parent_graph_id: str,
|
|
498
|
+
shared_filename: str,
|
|
499
|
+
delete_workspace: bool = True,
|
|
500
|
+
) -> PublishResult:
|
|
501
|
+
"""
|
|
502
|
+
Publish workspace to main graph via incremental ingestion.
|
|
503
|
+
|
|
504
|
+
This is the complete publish flow:
|
|
505
|
+
1. Export subgraph to parquet
|
|
506
|
+
2. Incremental ingest to main graph (filtered by filename)
|
|
507
|
+
3. Delete workspace (optional)
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
workspace_id: Subgraph workspace ID
|
|
511
|
+
parent_graph_id: Parent graph to publish to
|
|
512
|
+
shared_filename: Filename to use for export/ingest
|
|
513
|
+
delete_workspace: Whether to delete workspace after publish
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
PublishResult with statistics
|
|
517
|
+
"""
|
|
518
|
+
# Step 1: Export to parquet
|
|
519
|
+
await self.export_to_parquet(workspace_id, shared_filename)
|
|
520
|
+
|
|
521
|
+
# Step 2: Incremental ingest to main graph
|
|
522
|
+
ingest_request = {
|
|
523
|
+
"file_names": [shared_filename], # Filter to only this report
|
|
524
|
+
"ignore_errors": True,
|
|
525
|
+
"rebuild": False,
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
response = await self.api.post(
|
|
529
|
+
f"/v1/graphs/{parent_graph_id}/tables/ingest", json=ingest_request
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
ingest_result = response.json()
|
|
533
|
+
|
|
534
|
+
# Step 3: Delete workspace (optional)
|
|
535
|
+
if delete_workspace:
|
|
536
|
+
workspace_name = workspace_id.split("_")[-1]
|
|
537
|
+
await self.delete_workspace(parent_graph_id, workspace_name)
|
|
538
|
+
|
|
539
|
+
return PublishResult(
|
|
540
|
+
nodes_created=ingest_result.get("nodes_created", 0),
|
|
541
|
+
relationships_created=ingest_result.get("relationships_created", 0),
|
|
542
|
+
execution_time_ms=ingest_result.get("execution_time_ms", 0),
|
|
543
|
+
success=ingest_result.get("status") == "success",
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
def _batch_nodes(self, nodes: List[Dict], batch_size: int):
|
|
547
|
+
"""Helper to batch nodes for efficient creation"""
|
|
548
|
+
for i in range(0, len(nodes), batch_size):
|
|
549
|
+
yield nodes[i : i + batch_size]
|
|
550
|
+
|
|
551
|
+
def _build_batch_create_cypher(self, node_type: str, nodes: List[Dict]) -> str:
|
|
552
|
+
"""Helper to build batch CREATE cypher"""
|
|
553
|
+
creates = []
|
|
554
|
+
for i, node_data in enumerate(nodes):
|
|
555
|
+
node = node_data.get("n", node_data)
|
|
556
|
+
props = json.dumps(node).replace('"', "'")
|
|
557
|
+
creates.append(f"CREATE (n{i}:{node_type} {props})")
|
|
558
|
+
|
|
559
|
+
return "\n".join(creates)
|
|
560
|
+
|
|
561
|
+
def _build_fact_create_cypher(self, row: Dict) -> str:
|
|
562
|
+
"""Helper to build cypher for creating fact with all relationships"""
|
|
563
|
+
# This would build proper MERGE statements for fact and all related nodes
|
|
564
|
+
# Simplified for illustration
|
|
565
|
+
return f"""
|
|
566
|
+
MERGE (f:Fact {{identifier: '{row["f"]["identifier"]}'}})
|
|
567
|
+
SET f = {json.dumps(row["f"]).replace('"', "'")}
|
|
568
|
+
|
|
569
|
+
MERGE (el:Element {{identifier: '{row["el"]["identifier"]}'}})
|
|
570
|
+
SET el = {json.dumps(row["el"]).replace('"', "'")}
|
|
571
|
+
|
|
572
|
+
MERGE (f)-[:FACT_HAS_ELEMENT]->(el)
|
|
573
|
+
"""
|
|
574
|
+
|
|
575
|
+
async def fork_from_parent_with_sse(
|
|
576
|
+
self,
|
|
577
|
+
workspace_id: str,
|
|
578
|
+
parent_graph_id: str,
|
|
579
|
+
fork_options: Optional[Dict[str, Any]] = None,
|
|
580
|
+
progress_callback: Optional[callable] = None,
|
|
581
|
+
) -> Dict[str, Any]:
|
|
582
|
+
"""
|
|
583
|
+
Fork data from parent graph with SSE progress monitoring.
|
|
584
|
+
|
|
585
|
+
This method triggers a server-side fork operation and monitors progress
|
|
586
|
+
via Server-Sent Events (SSE) for real-time updates on large operations.
|
|
587
|
+
|
|
588
|
+
Args:
|
|
589
|
+
workspace_id: Target subgraph workspace ID
|
|
590
|
+
parent_graph_id: Source parent graph ID
|
|
591
|
+
fork_options: Options for selective forking:
|
|
592
|
+
- tables: List of tables or "all"
|
|
593
|
+
- period_filter: Date range filter
|
|
594
|
+
- entity_filter: Entity IDs to include
|
|
595
|
+
- exclude_patterns: Tables to exclude (e.g., ["Report*"])
|
|
596
|
+
progress_callback: Optional callback for progress updates
|
|
597
|
+
|
|
598
|
+
Returns:
|
|
599
|
+
Fork result with statistics
|
|
600
|
+
|
|
601
|
+
Example:
|
|
602
|
+
>>> async def on_progress(event):
|
|
603
|
+
... print(f"Progress: {event['message']}")
|
|
604
|
+
>>>
|
|
605
|
+
>>> result = await workspace_client.fork_from_parent_with_sse(
|
|
606
|
+
... workspace_id="kg123_dev",
|
|
607
|
+
... parent_graph_id="kg123",
|
|
608
|
+
... fork_options={
|
|
609
|
+
... "tables": "all",
|
|
610
|
+
... "exclude_patterns": ["Report*"],
|
|
611
|
+
... "period_filter": {"start": "2024-01-01", "end": "2024-12-31"}
|
|
612
|
+
... },
|
|
613
|
+
... progress_callback=on_progress
|
|
614
|
+
... )
|
|
615
|
+
"""
|
|
616
|
+
# Start fork operation and get SSE endpoint
|
|
617
|
+
fork_request = {
|
|
618
|
+
"operation": "fork",
|
|
619
|
+
"source_graph_id": parent_graph_id,
|
|
620
|
+
"target_graph_id": workspace_id,
|
|
621
|
+
"options": fork_options or {},
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
# Initiate fork operation
|
|
625
|
+
response = await self.api.post(
|
|
626
|
+
f"/v1/graphs/{workspace_id}/operations/fork", json=fork_request
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
operation = response.json()
|
|
630
|
+
operation_id = operation.get("operation_id")
|
|
631
|
+
|
|
632
|
+
if not operation_id:
|
|
633
|
+
# If no SSE available, fall back to synchronous wait
|
|
634
|
+
return operation
|
|
635
|
+
|
|
636
|
+
# Connect to SSE endpoint for progress monitoring
|
|
637
|
+
sse_url = f"{self.api.base_url}/v1/operations/{operation_id}/stream"
|
|
638
|
+
headers = getattr(self.api, "_headers", {})
|
|
639
|
+
|
|
640
|
+
async with httpx.AsyncClient() as client:
|
|
641
|
+
async with client.stream("GET", sse_url, headers=headers) as response:
|
|
642
|
+
async for line in response.aiter_lines():
|
|
643
|
+
if line.startswith("data: "):
|
|
644
|
+
data = line[6:].strip()
|
|
645
|
+
if data == "[DONE]":
|
|
646
|
+
break
|
|
647
|
+
|
|
648
|
+
try:
|
|
649
|
+
event = json.loads(data)
|
|
650
|
+
|
|
651
|
+
# Call progress callback if provided
|
|
652
|
+
if progress_callback:
|
|
653
|
+
await progress_callback(event)
|
|
654
|
+
|
|
655
|
+
# Check for completion
|
|
656
|
+
if event.get("status") in ["completed", "failed"]:
|
|
657
|
+
return event
|
|
658
|
+
|
|
659
|
+
except json.JSONDecodeError:
|
|
660
|
+
continue
|
|
661
|
+
|
|
662
|
+
# Get final status if SSE ended without completion event
|
|
663
|
+
final_response = await self.api.get(f"/v1/operations/{operation_id}")
|
|
664
|
+
return final_response.json()
|
|
665
|
+
|
|
666
|
+
async def write_view_to_workspace(
|
|
667
|
+
self, workspace_id: str, view_data: Dict[str, Any], report_name: str = None
|
|
668
|
+
) -> str:
|
|
669
|
+
"""
|
|
670
|
+
Write view/report data to subgraph workspace.
|
|
671
|
+
|
|
672
|
+
Creates Report, FactSet, Structure, and Association nodes.
|
|
673
|
+
|
|
674
|
+
Args:
|
|
675
|
+
workspace_id: Subgraph workspace ID
|
|
676
|
+
view_data: View data including facts, structures, associations
|
|
677
|
+
report_name: Optional report name
|
|
678
|
+
|
|
679
|
+
Returns:
|
|
680
|
+
Report ID created
|
|
681
|
+
"""
|
|
682
|
+
if not self.query:
|
|
683
|
+
raise ValueError("Query client required for writing view data")
|
|
684
|
+
|
|
685
|
+
import uuid
|
|
686
|
+
|
|
687
|
+
report_id = str(uuid.uuid4())
|
|
688
|
+
|
|
689
|
+
# Create Report node
|
|
690
|
+
report_cypher = f"""
|
|
691
|
+
CREATE (r:Report {{
|
|
692
|
+
identifier: '{report_id}',
|
|
693
|
+
name: '{report_name or "Draft Report"}',
|
|
694
|
+
status: 'draft',
|
|
695
|
+
created_at: datetime(),
|
|
696
|
+
ai_generated: false
|
|
697
|
+
}})
|
|
698
|
+
RETURN r.identifier as report_id
|
|
699
|
+
"""
|
|
700
|
+
|
|
701
|
+
await self.query.query(workspace_id, report_cypher)
|
|
702
|
+
|
|
703
|
+
# Create FactSet and Facts
|
|
704
|
+
if "facts" in view_data:
|
|
705
|
+
factset_id = str(uuid.uuid4())
|
|
706
|
+
factset_cypher = f"""
|
|
707
|
+
MATCH (r:Report {{identifier: '{report_id}'}})
|
|
708
|
+
CREATE (fs:FactSet {{
|
|
709
|
+
identifier: '{factset_id}',
|
|
710
|
+
name: 'View Facts'
|
|
711
|
+
}})
|
|
712
|
+
CREATE (r)-[:REPORT_HAS_FACT_SET]->(fs)
|
|
713
|
+
"""
|
|
714
|
+
await self.query.query(workspace_id, factset_cypher)
|
|
715
|
+
|
|
716
|
+
# Add facts to factset
|
|
717
|
+
for fact in view_data["facts"]:
|
|
718
|
+
fact_cypher = f"""
|
|
719
|
+
MATCH (fs:FactSet {{identifier: '{factset_id}'}})
|
|
720
|
+
CREATE (f:Fact {{
|
|
721
|
+
identifier: randomUUID(),
|
|
722
|
+
element_id: '{fact.get("element_id")}',
|
|
723
|
+
value: {fact.get("value", 0)},
|
|
724
|
+
period_end: '{fact.get("period_end", "")}'
|
|
725
|
+
}})
|
|
726
|
+
CREATE (fs)-[:FACT_SET_CONTAINS_FACT]->(f)
|
|
727
|
+
"""
|
|
728
|
+
await self.query.query(workspace_id, fact_cypher)
|
|
729
|
+
|
|
730
|
+
# Create Structure and Associations
|
|
731
|
+
if "structures" in view_data:
|
|
732
|
+
for structure in view_data["structures"]:
|
|
733
|
+
struct_cypher = f"""
|
|
734
|
+
MATCH (r:Report {{identifier: '{report_id}'}})
|
|
735
|
+
CREATE (s:Structure {{
|
|
736
|
+
identifier: randomUUID(),
|
|
737
|
+
name: '{structure.get("name", "View Structure")}',
|
|
738
|
+
type: 'presentation'
|
|
739
|
+
}})
|
|
740
|
+
CREATE (r)-[:REPORT_HAS_STRUCTURE]->(s)
|
|
741
|
+
"""
|
|
742
|
+
await self.query.query(workspace_id, struct_cypher)
|
|
743
|
+
|
|
744
|
+
return report_id
|