def convert_tdb_request_spec_to_inference_sub_request(
    tdb_request_spec: TdbRequestSpec,
) -> InferenceSubRequest:
    """
    The client sends a TdbRequestSpec, but internally we do all processing in terms of
    InferenceSubRequests. This function converts from the client representation to the server
    representation.
    """
    loss_fn_config: LossFnConfig | None = LossFnConfig(
        name=LossFnName.LOGIT_DIFF,
        target_tokens=tdb_request_spec.target_tokens,
        distractor_tokens=tdb_request_spec.distractor_tokens,
    )
    ablation_specs = [
        node_ablation_to_ablation_spec(ablation)
        for ablation in (tdb_request_spec.node_ablations or [])
    ] + [
        AblationSpec(
            index=MirroredActivationIndex(
                activation_location_type=ActivationLocationType.RESID_FINAL_LAYER_NORM_SCALE,
                pass_type=PassType.BACKWARD,
                tensor_indices=("All", "All"),  # ablate at all positions in the sequence
                layer_index=None,
            ),
            value=0,
        )
    ]
    current_token_index = -1
    trace_config = None
    if tdb_request_spec.upstream_node_to_trace is None:
        assert tdb_request_spec.downstream_node_to_trace is None
    else:
        (
            trace_config,
            trace_token_index,
        ) = nodes_to_trace_to_trace_config(
            tdb_request_spec.upstream_node_to_trace, tdb_request_spec.downstream_node_to_trace
        )
        if trace_token_index is not None:
            current_token_index = trace_token_index

    if trace_config is None:  # not tracing -> DO compute loss
        pass
    elif trace_config.attention_trace_type == AttentionTraceType.V:  # tracing attention through V
        if trace_config.downstream_trace_config is None:
            pass  # tracing through V with no downstream trace -> DO compute loss
        else:
            loss_fn_config = (
                None  # tracing through V, but also with downstream trace -> DON'T compute loss
            )
    else:
        loss_fn_config = (
            None  # tracing something other than attention through V -> DON'T compute loss
        )

    inference_request_spec = InferenceRequestSpec(
        prompt=tdb_request_spec.prompt,
        loss_fn_config=loss_fn_config,
        ablation_specs=ablation_specs,
        trace_config=MirroredTraceConfig.from_trace_config(trace_config) if trace_config else None,
    )

    spec_by_component_for_top_k = MultipleTopKDerivedScalarsRequestSpec(
        token_index=None,
        dst_list_by_group_id=make_grouped_dsts_per_component(
            tdb_request_spec.component_type_for_mlp,
            tdb_request_spec.component_type_for_attention,
        ),
        top_and_bottom_k=tdb_request_spec.top_and_bottom_k_for_node_table,
    )

    spec_by_component_always_mlp_for_token_display = MultipleTopKDerivedScalarsRequestSpec(
        token_index=None,
        # the response to this request is to be used for summarizing the effects of entire
        # attention and MLP layers per token; thus, using a different basis for the activations
        # within a layer is not helpful, and we use MLP activations themselves.
        dst_list_by_group_id=make_grouped_dsts_per_component(
            ComponentTypeForMlp.NEURON, ComponentTypeForAttention.ATTENTION_HEAD
        ),
        top_and_bottom_k=1,
        dimensions_to_keep_for_intermediate_sum=[
            Dimension.SEQUENCE_TOKENS,
            Dimension.ATTENDED_TO_SEQUENCE_TOKENS,
        ],
    )

    def scored_tokens_request_spec(
        token_scoring_type: TokenScoringType,
    ) -> ScoredTokensRequestSpec:
        return ScoredTokensRequestSpec(
            token_scoring_type=token_scoring_type,
            num_tokens=10,
            # Our scored tokens requests are associated with the "topKComponents" request spec. This
            # means that they use the same node indices, DSTs and DST configs.
            depends_on_spec_name="topKComponents",
        )

    def token_pair_attribution_request_spec() -> TokenPairAttributionRequestSpec:
        return TokenPairAttributionRequestSpec(
            num_tokens_attended_to=3,
            # Our scored tokens requests are associated with the "topKComponents" request spec. This
            # means that they use the same node indices, DSTs and DST configs.
            depends_on_spec_name="topKComponents",
        )

    processing_request_spec_by_name: dict[str, ProcessingRequestSpec] = {
        "topKComponents": spec_by_component_for_top_k,
        "componentSumsForTokenDisplay": spec_by_component_always_mlp_for_token_display,
        # It's important for these request specs to come after the "topKComponents" request spec,
        # since they depend on data generated for that request spec.
        "upvotedOutputTokens": scored_tokens_request_spec(TokenScoringType.UPVOTED_OUTPUT_TOKENS),
        "inputTokensThatUpvoteMlp": scored_tokens_request_spec(
            TokenScoringType.INPUT_TOKENS_THAT_UPVOTE_MLP
        ),
        "inputTokensThatUpvoteAttnQ": scored_tokens_request_spec(
            TokenScoringType.INPUT_TOKENS_THAT_UPVOTE_ATTN_Q
        ),
        "inputTokensThatUpvoteAttnK": scored_tokens_request_spec(
            TokenScoringType.INPUT_TOKENS_THAT_UPVOTE_ATTN_K
        ),
        "tokenPairAttribution": token_pair_attribution_request_spec(),
    }

    spec_by_vocab_token = MultipleTopKDerivedScalarsRequestSpec(
        dst_list_by_group_id={GroupId.LOGITS: [DerivedScalarType.LOGITS]},
        top_and_bottom_k=100,
        token_index=current_token_index,
    )
    processing_request_spec_by_name["topOutputTokenLogits"] = spec_by_vocab_token

    return InferenceSubRequest(
        inference_request_spec=inference_request_spec,
        processing_request_spec_by_name=processing_request_spec_by_name,
    )

    The client sends a TdbRequestSpec, but internally we do all processing in terms of
    InferenceSubRequests. This function converts from the client representation to the server
    representation.
    
neuron_explainer/activation_server/tdb_conversions.py