Error in Viewing Exchange File created from 3d View in Revit
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hello,
I am creating a web application using the Autodesk APIs, that creates an exchange file from the 3D view, and then we can check that model, according to the parameters specified by us. But, I am facing an issue where, after I have created the exchange file from the 3D view in Revit, I am unable to view it. For reference to how I have created the exchange file, I have shared the code snippet below, or you can see the process through https://forums.autodesk.com/t5/data-exchange-forum/create-exchange-file-from-3d-view-in-revit-error/... .
i) The result that I am getting after clicking create exchange is:
{'success': True, 'data': {'data': {'createExchange': {'exchange': {'id': 'ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA', 'name': '5TH STOREY_Exchange', 'alternativeIdentifiers': {'fileUrn': 'urn:adsk.wipprod:dm.lineage:KDkTkAZ0TbSbcu4zVFmFvQ', 'fileVersionUrn': 'urn:adsk.wipprod:fs.file:vf.KDkTkAZ0TbSbcu4zVFmFvQ?version=1'}}}}}, 'exchange_id': 'ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA', 'exchange_name': '5TH STOREY_Exchange', 'urn': 'dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLktEa1RrQVowVGJTYmN1NHpWRm1GdlE_dmVyc2lvbj0x', 'raw_urn': 'urn:adsk.wipprod:fs.file:vf.KDkTkAZ0TbSbcu4zVFmFvQ?version=1'}
================================================================================
[EXCHANGE CREATION RESULT]
Exchange ID: ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA
Raw URN: urn:adsk.wipprod:fs.file:vf.KDkTkAZ0TbSbcu4zVFmFvQ?version=1
URL-safe Base64-encoded URN: dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLktEa1RrQVowVGJTYmN1NHpWRm1GdlE_dmVyc2lvbj0x
================================================================================
127.0.0.1 - - [01/Dec/2025 11:09:04] "POST /api/create_exchange HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 11:09:04] "OPTIONS /api/FolderContent?id=Zm9sZH5iLjNlYTAyZWQxLWQwMzAtNGE0Ni05NjNiLTgwMTVmMTEzYTEwY35iLjk5YjVlMDA1LWQyZWItNDk4YS1iZjU1LTAxZWQ5MDkxMTRhZn51cm46YWRzay53aXBwcm9kOmZzLmZvbGRlcjpjby5JR0g3RVlreFFJUzBJY3VWbUNtUzdR HTTP/1.1" 200 -
Using token: eyJhbGciOiJSUzI1NiIsImtpZCI6IlZiakZvUzhQU3lYODQyMV... for folder: Zm9sZH5iLjNlYTAyZWQxLWQwMzAtNGE0Ni05NjNiLTgwMTVmMTEzYTEwY35iLjk5YjVlMDA1LWQyZWItNDk4YS1iZjU1LTAxZWQ5MDkxMTRhZn51cm46YWRzay53aXBwcm9kOmZzLmZvbGRlcjpjby5JR0g3RVlreFFJUzBJY3VWbUNtUzdR
Sending request to https://developer.api.autodesk.com/dataexchange/2023-05/graphql for folder Zm9sZH5iLjNlYTAyZWQxLWQwMzAtNGE0Ni05NjNiLTgwMTVmMTEzYTEwY35iLjk5YjVlMDA1LWQyZWItNDk4YS1iZjU1LTAxZWQ5MDkxMTRhZn51cm46YWRzay53aXBwcm9kOmZzLmZvbGRlcjpjby5JR0g3RVlreFFJUzBJY3VWbUNtUzdR
Response status: 200
Processed content: {
"folders": [],
"exchanges": [
{
"id": "ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA",
"name": "5TH STOREY_Exchange",
"alternativeIdentifiers": {
"fileUrn": "urn:adsk.wipprod:dm.lineage:KDkTkAZ0TbSbcu4zVFmFvQ",
"fileVersionUrn": "urn:adsk.wipprod:fs.file:vf.KDkTkAZ0TbSbcu4zVFmFvQ?version=1"
},
"__typename": "Exchange"
}
ii) Result after clicking on the created exchange, in order to view it:
127.0.0.1 - - [01/Dec/2025 11:09:07] "GET /api/FolderContent?id=Zm9sZH5iLjNlYTAyZWQxLWQwMzAtNGE0Ni05NjNiLTgwMTVmMTEzYTEwY35iLjk5YjVlMDA1LWQyZWItNDk4YS1iZjU1LTAxZWQ5MDkxMTRhZn51cm46YWRzay53aXBwcm9kOmZzLmZvbGRlcjpjby5JR0g3RVlreFFJUzBJY3VWbUNtUzdR HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 11:34:02] "GET /api/config HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 11:34:02] "OPTIONS /api/get_exchange_urn HTTP/1.1" 200 -
[GET EXCHANGE URN] Request received for Exchange ID: ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA
================================================================================
[EXCHANGE URN QUERY] Exchange ID: ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA
data from the response ********** {'data': {'exchange': {'alternativeIdentifiers': {'fileUrn': 'urn:adsk.wipprod:dm.lineage:KDkTkAZ0TbSbcu4zVFmFvQ', 'fileVersionUrn': 'urn:adsk.wipprod:fs.file:vf.KDkTkAZ0TbSbcu4zVFmFvQ?version=1'}}}}
fileVersionUrn ********** urn:adsk.wipprod:fs.file:vf.KDkTkAZ0TbSbcu4zVFmFvQ?version=1
fileUrn ********** urn:adsk.wipprod:dm.lineage:KDkTkAZ0TbSbcu4zVFmFvQ
[URN INFO] Raw URN: urn:adsk.wipprod:dm.lineage:KDkTkAZ0TbSbcu4zVFmFvQ
[URN INFO] URL-safe Base64-encoded URN: dXJuOmFkc2sud2lwcHJvZDpkbS5saW5lYWdlOktEa1RrQVowVGJTYmN1NHpWRm1GdlE
================================================================================
[GET EXCHANGE URN] Successfully retrieved URN for Exchange ID: ZXhjfjBhNUl4THIzSnNlY1FuSnlZa3pwYldfTDJDfjcwODc5N2E0LWQ2N2UtMzZlZS05OGE3LTJkN2QxZjIzMjg0NA
[GET EXCHANGE URN] URN: dXJuOmFkc2sud2lwcHJvZDpkbS5saW5lYWdlOktEa1RrQVowVGJTYmN1NHpWRm1GdlE
127.0.0.1 - - [01/Dec/2025 11:34:06] "POST /api/get_exchange_urn HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 11:34:06] "OPTIONS /api/conversation/start HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 11:34:08] "POST /api/conversation/start HTTP/1.1" 200 -
iii) The exchange screen shows:
iv) The code that I am using for these are:
1) Exchange creation from file (model_derivative.py):
def create_exchange_from_file(endpoint_url, project_id, item_id, folder_id, view_name, exchange_name, aps_token):
"""
Create an exchange from a Revit file using GraphQL IDs (not URNs):
1. Get folder data using getFolderById to ensure we have the correct GraphQL folder ID
2. Verify the source file item exists in the folder (item_id should be GraphQL ID from folder.items.results[].id)
3. Use folder GraphQL ID and item GraphQL ID in createExchange mutation
According to Autodesk documentation:
- sourceFileId: The GraphQL item ID from folder.items.results[].id (NOT a URN)
- targetFolderId: The GraphQL folder ID from folder.id (NOT a URN)
"""
try:
# Step 1: Get folder data to extract and verify GraphQL IDs
print(f"[EXCHANGE] Getting folder data for folder_id: {folder_id}")
folder_result = get_folder_by_id_graphql(endpoint_url, folder_id, aps_token)
if not folder_result.get("success"):
error_type = folder_result.get("error", "UNKNOWN_ERROR")
error_message = folder_result.get("message", "Failed to get folder information")
# Provide user-friendly messages for folder errors
if error_type == "NO_FOLDER_DATA":
user_message = "The folder could not be found. Please select a valid folder and try again."
elif error_type == "GRAPHQL_ERROR":
user_message = "Unable to access folder information. Please check your permissions and try again."
else:
user_message = error_message
return {
"error": error_type,
"message": error_message,
"user_message": user_message,
"details": folder_result.get("details")
}
# Extract GraphQL folder ID (this is the targetFolderId)
graphql_folder_id = folder_result["folder_id"]
print(f"[SUCCESS] GraphQL Folder ID: {graphql_folder_id}")
# Step 2: Verify the source file item exists in the folder
# The item_id passed should be a GraphQL ID from folder.items.results[].id
items = folder_result.get("items", [])
print(f"[INFO] Found {len(items)} items in folder")
# Check if item_id matches any item in the folder
source_file_graphql_id = None
for item in items:
if item.get("id") == item_id:
source_file_graphql_id = item["id"]
print(f"[SUCCESS] Found source file in folder: {item.get('name')} (ID: {source_file_graphql_id})")
break
if not source_file_graphql_id:
# If not found, but item_id doesn't start with "urn:", assume it's a GraphQL ID
# (might be from a different folder or parent folder)
if item_id and not item_id.startswith("urn:"):
source_file_graphql_id = item_id
print(f"[INFO] Using item_id directly as GraphQL ID: {source_file_graphql_id}")
print(f"[WARNING] Item not found in folder items list, but using provided ID")
else:
return {
"error": "ITEM_NOT_FOUND",
"message": "Source file item not found in folder",
"user_message": "The source file could not be found in the selected folder. Please ensure:\n1. The file exists in the target folder\n2. You're using the correct file from the folder"
}
print(f"[SUCCESS] Using Source File GraphQL ID: {source_file_graphql_id}")
# Step 3: Prepare GraphQL mutation with GraphQL IDs (not URNs)
mutation = """
mutation createExchangeFromRevit($source_file_id: String!, $folder_id: String!, $view_name: String!, $target_name: String!) {
createExchange(
input: {
viewName: $view_name
source: { fileId: $source_file_id }
target: { name: $target_name, folderId: $folder_id }
}
) {
exchange {
id
name
alternativeIdentifiers {
fileUrn
fileVersionUrn
}
}
}
}
"""
variables = {
"source_file_id": source_file_graphql_id, # GraphQL item ID, not URN
"folder_id": graphql_folder_id, # GraphQL folder ID, not URN
"view_name": view_name,
"target_name": exchange_name
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {aps_token}"
}
print("[PAYLOAD] FINAL PAYLOAD (Using GraphQL IDs):")
print(f" viewName: {view_name}")
print(f" sourceFileId (GraphQL ID): {source_file_graphql_id}")
print(f" targetExchangeName: {exchange_name}")
print(f" targetFolderId (GraphQL ID): {graphql_folder_id}")
# Step 4: Call Data Exchange GraphQL endpoint
response = requests.post(endpoint_url, json={"query": mutation, "variables": variables}, headers=headers)
print(f"[STATUS] Response Status: {response.status_code}")
data = response.json()
print("response", data)
if response.status_code != 200:
return {"error": "API_ERROR", "message": f"{response.status_code} - {response.text}"}
if "errors" in data:
print(f"[ERROR] GraphQL Errors: {data['errors']}")
# Parse GraphQL errors to provide specific error messages
graphql_errors = data["errors"]
error_info = parse_graphql_errors(graphql_errors)
return {
"error": error_info.get("error_type", "GRAPHQL_ERROR"),
"message": error_info.get("message", "Failed to create exchange"),
"details": graphql_errors,
"user_message": error_info.get("user_message", error_info.get("message"))
}
print(f"[SUCCESS] Exchange created successfully: {data}")
exchange_data = data.get("data", {}).get("createExchange", {}).get("exchange", {})
# Extract exchange ID
exchange_id = exchange_data.get("id")
print(f"{'='*80}")
print(f"[EXCHANGE INFO] Exchange ID: {exchange_id}")
print(f"{'='*80}")
# Extract alternative identifiers for URN
alt_ids = exchange_data.get("alternativeIdentifiers", {})
file_version_urn = alt_ids.get("fileVersionUrn","")
file_urn = alt_ids.get("fileUrn","")
raw_urn = file_version_urn or file_urn
# Log the raw URN for debugging
if raw_urn:
print(f"[URN INFO] Raw URN: {raw_urn}")
# Encode the URN for viewer use
import base64
encoded_urn = base64.urlsafe_b64encode(raw_urn.encode("utf-8")).decode("utf-8").rstrip("=")
print(f"[URN INFO] URL-safe Base64-encoded URN: {encoded_urn}")
else:
print(f"[WARNING] No URN found in alternativeIdentifiers - may need to query separately")
encoded_urn = None
print(f"{'='*80}\n")
return {
"success": True,
"data": data,
"exchange_id": exchange_id,
"exchange_name": exchange_data.get("name"),
"urn": encoded_urn, # Include the encoded URN if available
"raw_urn": raw_urn # Include raw URN for reference
}
except Exception as e:
return {"error": "EXCEPTION", "message": str(e)}
2) Getting exchange ID, and URN (app.py):
@app.route('/api/create_exchange', methods=['POST'])
def create_exchange():
try:
data = request.get_json()
file_id = data.get('fileId')
project_id = data.get('projectId')
folder_id = data.get('folderId')
view_name = data.get('viewName')
exchange_name = view_name+"_Exchange"
# Get token from config
aps_token = config.get('aps_token')
if not aps_token:
return jsonify({"error": "No authorization token available"}), 401
print(f"[EXCHANGE] Exchange creation request:")
print(f"[INFO] File ID: {file_id}")
print(f"[INFO] Project ID: {project_id}")
print(f"[INFO] Folder ID: {folder_id}")
print(f"[INFO] View Name: {view_name}")
print(f"[INFO] Exchange Name: {exchange_name}")
# The file_id and folder_id from frontend should already be GraphQL IDs
# from the getFolderById query response
# file_id comes from items.results[].id (GraphQL item ID)
# folder_id comes from folder.id (GraphQL folder ID)
# Use the GraphQL IDs directly - no decoding needed
result = create_exchange_from_file(
ENDPOINT_URL,
project_id, # Keep project_id for reference, but not used for GraphQL IDs
file_id, # This should be the GraphQL item ID from folder.items.results[].id
folder_id, # This should be the GraphQL folder ID from folder.id
view_name,
exchange_name,
aps_token,
)
# result = create_revit_exchange(aps_token)
print("589 result", result)
if not result:
return jsonify({"error": "Failed to create exchange"}), 500
if result.get("success"):
exchange_id = result.get("exchange_id")
encoded_urn = result.get("urn")
raw_urn = result.get("raw_urn")
# Print exchange ID and URN for verification
print(f"{'='*80}")
print(f"[EXCHANGE CREATION RESULT]")
print(f"Exchange ID: {exchange_id}")
if raw_urn:
print(f"Raw URN: {raw_urn}")
if encoded_urn:
print(f"URL-safe Base64-encoded URN: {encoded_urn}")
print(f"{'='*80}")
response_data = {
"success": True,
"exchangeId": exchange_id,
"exchangeName": result.get("exchange_name"),
"elementsCount": result.get("elements_count", 0),
"message": "Exchange created successfully"
}
# Include URN if available from exchange creation
if encoded_urn:
response_data["urn"] = encoded_urn
return jsonify(response_data)
else:
# Use user_message if available, otherwise fall back to message
error_message = result.get("user_message") or result.get("message", "Failed to create exchange")
return jsonify({
"success": False,
"error": result.get("error"),
"error_type": result.get("error"), # Include error type for frontend handling
"message": error_message,
"details": result.get("details")
}), 500
except Exception as e:
print(f"Error creating exchange: {e}")
return jsonify({
"success": False,
"error": str(e),
"message": "Error creating exchange"
}), 500
@app.route('/api/get_exchange_urn', methods=['POST'])
def get_exchange_urn():
data = request.get_json() or {}
exchange_id = data.get('exchangeId')
auth_token = data.get('authToken')
if not exchange_id:
return jsonify({"error": "Please provide exchangeId"}), 400
# If no auth token provided, try to use the one from config
if not auth_token:
auth_token = AUTH_TOKEN
if not auth_token:
return jsonify({"error": "No auth token provided"}), 400
print(f"[GET EXCHANGE URN] Request received for Exchange ID: {exchange_id}")
urn = exchange_urn_from_id(
endpoint_url=ENDPOINT_URL,
exchange_id=exchange_id,
auth_token=auth_token
)
if urn:
print(f"[GET EXCHANGE URN] Successfully retrieved URN for Exchange ID: {exchange_id}")
print(f"[GET EXCHANGE URN] URN: {urn}")
else:
print(f"[GET EXCHANGE URN] Failed to retrieve URN for Exchange ID: {exchange_id}")
# Fetch metadata synchronously
properties_data = fetch_exchange_properties(endpoint_url=ENDPOINT_URL, exchange_id=exchange_id, auth_token=auth_token)
# print("Fetched properties:", properties_data)
# Store results in app config
if properties_data:
# Store both property names and their units
app.config['EXCHANGE_PROPERTIES'] = properties_data['property_names']
app.config['PROPERTY_UNITS'] = properties_data['property_units'] # Store unit information
app.config['EXCHANGE_PROPERTIES'].append("category") # Ensure "category" is always included
# Remove duplicates and sort properties case-insensitively
app.config['EXCHANGE_PROPERTIES'] = sorted(list(set(app.config['EXCHANGE_PROPERTIES'])), key=lambda x: str(x).lower())
if urn:
return jsonify({
"urn": urn,
"categories": app.config['EXCHANGE_CATEGORIES'],
"properties": app.config['EXCHANGE_PROPERTIES']
})
return jsonify({"error": "Failed to fetch URN"}), 500
3) Creating/Retrieving URN (graphql_queries.py):
def exchange_urn_from_id(endpoint_url, exchange_id, auth_token=None):
"""
Returns the Base64-encoded URN for a given exchange ID.
Args:
endpoint_url: The URL of the GraphQL API.
exchange_id: The ID of the exchange.
auth_token: (Optional) The authentication token.
Returns:
The Base64-encoded URN string (without padding) or None if failed.
"""
graphql_query = """
query Exchange($exchangeId: ID!) {
exchange(exchangeId: $exchangeId) {
alternativeIdentifiers {
fileUrn
fileVersionUrn
}
}
}
"""
variables = {"exchangeId": exchange_id}
headers = {"Content-Type": "application/json"}
if auth_token:
headers["Authorization"] = f"Bearer {auth_token}"
try:
print(f"{'='*80}")
print(f"[EXCHANGE URN QUERY] Exchange ID: {exchange_id}")
response = requests.post(
endpoint_url,
headers=headers,
json={"query": graphql_query, "variables": variables}
)
data = response.json()
print("data from the response **********", data)
ids = data["data"]["exchange"]["alternativeIdentifiers"]
fileVersionUrn = ids.get("fileVersionUrn","")
fileUrn = ids.get("fileUrn","")
print("fileVersionUrn **********", fileVersionUrn)
print("fileUrn **********", fileUrn)
# raw_urn = ids.get("fileVersionUrn","") or ids.get("fileUrn","")
if not fileUrn:
print("No fileUrn found for exchange ID: {exchange_id}")
return None
else :
raw_urn = fileUrn
# raw_urn = fileVersionUrn or fileUrn
# if not raw_urn:
# print(f"[WARNING] No URN found for exchange ID: {exchange_id}")
# print(f"{'='*80}")
# return None
print(f"[URN INFO] Raw URN: {raw_urn}")
#TODO : check this encoding and decoding happening same place ?
b64 = base64.urlsafe_b64encode(raw_urn.encode("utf-8")).decode("utf-8")
encoded_urn = b64.rstrip("=")
print(f"[URN INFO] URL-safe Base64-encoded URN: {encoded_urn}")
print(f"{'='*80}")
return encoded_urn
except Exception as e:
print(f"[ERROR] Failed to get URN for exchange ID {exchange_id}: {e}")
print(f"{'='*80}")
return None
4) Manifest file creation (model_derivative.py):
def get_model_manifest(base64_urn, access_token):
"""Call Model Derivative API manifest endpoint and return the full response."""
url = f"https://developer.api.autodesk.com/modelderivative/v2/designdata/{base64_urn}/manifest"
headers = {"Authorization": f"Bearer {access_token}"}
print(f"[EXTRACT] Getting manifest for URN: {base64_urn}")
try:
resp = requests.get(url, headers=headers)
print(f"[STATUS] Response Status: {resp.status_code}")
result = {
"status_code": resp.status_code,
"url": url,
"response_data": None,
"error": None
}
# Handle different response types
if resp.headers.get('content-type', '').startswith('application/json'):
try:
result["response_data"] = resp.json()
except ValueError as e:
result["error"] = f"Invalid JSON response: {str(e)}"
result["response_data"] = resp.text
print(f"[ERROR] JSON parsing failed: {str(e)}")
else:
result["response_data"] = resp.text
if resp.status_code == 200:
print("[SUCCESS] Manifest retrieved successfully")
elif resp.status_code == 202:
result["error"] = "Translation still in progress"
print("[PROGRESS] Translation is still in progress")
elif resp.status_code == 404:
result["error"] = "Manifest not found - translation may not have started"
print("[ERROR] Manifest not found (likely not translated yet)")
else:
result["error"] = f"API returned status {resp.status_code}"
print(f"[WARNING] API Error: {resp.status_code} - {resp.text}")
return result
except requests.exceptions.RequestException as e:
print(f"[ERROR] Request Error: {e}")
return {
"status_code": None,
"url": url,
"response_data": None,
"error": str(e)
}
5) Extracting manifest file:
def extract_views_from_manifest(manifest):
"""Extract 3D views from manifest recursively."""
print("[EXTRACT] Extracting views from manifest...")
print(f"[INFO] Manifest keys: {list(manifest.keys()) if isinstance(manifest, dict) else 'Not a dict'}")
# Get the actual response data from the manifest wrapper
response_data = manifest.get("response_data", {})
if not response_data:
print("[ERROR] No response_data in manifest")
return {"status": "error", "message": "No response data", "views": []}
print(f"[INFO] Response data keys: {list(response_data.keys()) if isinstance(response_data, dict) else 'Not a dict'}")
print(f"[INFO] Manifest status: {response_data.get('status')}")
print(f"[INFO] Progress: {response_data.get('progress')}")
# Check for processing errors
if 'messages' in response_data:
messages = response_data.get('messages', [])
print(f"[INFO] Processing messages: {messages}")
for msg in messages:
if msg.get('type') == 'error':
print(f"[ERROR] Processing error: {msg.get('message', 'Unknown error')}")
# Check for failed derivatives and provide detailed error information
derivatives = response_data.get("derivatives", [])
failed_derivatives = []
timeout_derivatives = []
for i, derivative in enumerate(derivatives):
status = derivative.get('status', 'unknown')
if status == 'failed':
error_msg = derivative.get('messages', 'No error details')
print(f"[ERROR] Derivative {i} failed: {error_msg}")
failed_derivatives.append({
"index": i,
"name": derivative.get('name', 'Unnamed'),
"outputType": derivative.get('outputType', 'unknown'),
"error": error_msg
})
elif status == 'timeout':
print(f"[TIMEOUT] Derivative {i} timed out during processing")
timeout_derivatives.append({
"index": i,
"name": derivative.get('name', 'Unnamed'),
"outputType": derivative.get('outputType', 'unknown')
})
# If all derivatives failed, return error
if len(failed_derivatives) == len(derivatives) and len(derivatives) > 0:
return {
"status": "all_derivatives_failed",
"message": "All derivatives failed to process",
"failed_derivatives": failed_derivatives,
"views": []
}
# If some derivatives timed out, warn but continue
if timeout_derivatives:
print(f"[WARNING] {len(timeout_derivatives)} derivatives timed out, but continuing with available ones")
# Handle translation status response
if response_data.get("status") == "inprogress":
return {
"status": "translation_in_progress",
"message": "File is being translated",
"views": []
}
if response_data.get("status") == "failed":
return {
"status": "translation_failed",
"message": "Translation failed",
"views": []
}
views = []
6) Viewing Exchange File (urn_viewer.js):
window.loadModelFromBackend = async function(exchangeId, retryCount = 0) {
try {
console.log(`🔄 Loading exchange ${exchangeId} (attempt ${retryCount + 1})`);
// Update the EXCHANGE_ID
EXCHANGE_ID = exchangeId;
localStorage.setItem('exchange_id', exchangeId);
// First get the config to ensure we have the token
const configResponse = await fetch('http://localhost:4000/api/config');
if (!configResponse.ok) {
throw new Error('Failed to fetch config');
}
const config = await configResponse.json();
APS_TOKEN = config.aps_token;
if (!APS_TOKEN) {
throw new Error('No authentication token available');
}
// Then fetch the URN with retry logic
const response = await fetch('http://localhost:4000/api/get_exchange_urn', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${APS_TOKEN}`
},
body: JSON.stringify({
exchangeId: exchangeId,
authToken: APS_TOKEN
})
});
if (!response.ok) {
// If it's a newly created exchange, it might not be ready yet
if (response.status === 404 && retryCount < 3) {
console.log(`⏳ Exchange not ready yet, retrying in 10 seconds... (${retryCount + 1}/3)`);
await new Promise(resolve => setTimeout(resolve, 10000));
return window.loadModelFromBackend(exchangeId, retryCount + 1);
}
throw new Error(`Failed to fetch URN: ${response.status}`);
}
const data = await response.json();
if (data.error) {
// If it's a newly created exchange, it might not be ready yet
if (data.error.includes('not found') && retryCount < 3) {
console.log(`⏳ Exchange not ready yet, retrying in 10 seconds... (${retryCount + 1}/3)`);
await new Promise(resolve => setTimeout(resolve, 10000));
return window.loadModelFromBackend(exchangeId, retryCount + 1);
}
throw new Error(data.error);
}
if (!data.urn) {
if (retryCount < 3) {
console.log(`⏳ URN not ready yet, retrying in 10 seconds... (${retryCount + 1}/3)`);
await new Promise(resolve => setTimeout(resolve, 10000));
return window.loadModelFromBackend(exchangeId, retryCount + 1);
}
throw new Error('No URN returned from server');
}
console.log(`✅ Got URN: ${data.urn}`);
// Save metadata if available
if (data.categories) {
window.EXCHANGE_CATEGORIES = data.categories;
}
// Initialize chat only after exchange is selected and loaded
if (window.initializeChat && typeof window.initializeChat === 'function') {
window.initializeChat();
}
if (data.properties) {
window.EXCHANGE_PROPERTIES = data.properties;
}
await loadModel(data.urn);
// Let other components know the model has been loaded
window.postMessage({
type: 'modelLoaded',
payload: {
urn: data.urn,
exchangeId: exchangeId,
token: APS_TOKEN
}
}, '*');
return data.urn;
window.postMessage({
type: 'modelLoaded',
payload: {
urn: data.urn,
exchangeId: exchangeId
}
}, '*');
} catch (err) {
alert('Failed to load model: ' + err.message);
throw err;
}
}
API Documentation Reference:
- APS Data Exchange GraphQL API: createExchange mutation
- Using APS Data Management API to get file/item information